@@ -156,6 +156,29 @@ pub enum SanType {
156
156
DnsName ( Ia5String ) ,
157
157
URI ( Ia5String ) ,
158
158
IpAddress ( IpAddr ) ,
159
+ OtherName ( ( Vec < u64 > , OtherNameValue ) ) ,
160
+ }
161
+
162
+ /// An OtherName value, defined in [RFC 5280§4.1.2.4].
163
+ ///
164
+ /// Technically, this could be any ASN.1 types but for now we cover only UTF-8 strings
165
+ /// as it will cover most of use cases, for instance smart cards.
166
+ ///
167
+ /// [RFC 5280§4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
168
+ #[ derive( Debug , PartialEq , Eq , Hash , Clone ) ]
169
+ #[ non_exhaustive]
170
+ pub enum OtherNameValue {
171
+ /// A string encoded using UTF-8
172
+ Utf8String ( String ) ,
173
+ }
174
+
175
+ impl < T > From < T > for OtherNameValue
176
+ where
177
+ T : Into < String > ,
178
+ {
179
+ fn from ( t : T ) -> Self {
180
+ OtherNameValue :: Utf8String ( t. into ( ) )
181
+ }
159
182
}
160
183
161
184
#[ cfg( feature = "x509-parser" ) ]
@@ -172,6 +195,7 @@ fn ip_addr_from_octets(octets: &[u8]) -> Result<IpAddr, Error> {
172
195
impl SanType {
173
196
#[ cfg( feature = "x509-parser" ) ]
174
197
fn try_from_general ( name : & x509_parser:: extensions:: GeneralName < ' _ > ) -> Result < Self , Error > {
198
+ use x509_parser:: der_parser:: asn1_rs:: { self , FromDer , Tag , TaggedExplicit } ;
175
199
Ok ( match name {
176
200
x509_parser:: extensions:: GeneralName :: RFC822Name ( name) => {
177
201
SanType :: Rfc822Name ( ( * name) . try_into ( ) ?)
@@ -183,19 +207,38 @@ impl SanType {
183
207
x509_parser:: extensions:: GeneralName :: IPAddress ( octets) => {
184
208
SanType :: IpAddress ( ip_addr_from_octets ( octets) ?)
185
209
} ,
210
+ x509_parser:: extensions:: GeneralName :: OtherName ( oid, value) => {
211
+ let oid = oid. iter ( ) . ok_or ( Error :: CouldNotParseCertificate ) ?;
212
+ // We first remove the explicit tag ([0] EXPLICIT)
213
+ let ( _, other_name) = TaggedExplicit :: < asn1_rs:: Any , _ , 0 > :: from_der ( & value)
214
+ . map_err ( |_| Error :: CouldNotParseCertificate ) ?;
215
+ let other_name = other_name. into_inner ( ) ;
216
+
217
+ let data = other_name. data ;
218
+ let try_str =
219
+ |data| std:: str:: from_utf8 ( data) . map_err ( |_| Error :: CouldNotParseCertificate ) ;
220
+ let other_name_value = match other_name. tag ( ) {
221
+ Tag :: Utf8String => OtherNameValue :: Utf8String ( try_str ( data) ?. to_owned ( ) ) ,
222
+ _ => return Err ( Error :: CouldNotParseCertificate ) ,
223
+ } ;
224
+
225
+ SanType :: OtherName ( ( oid. collect ( ) , other_name_value) )
226
+ } ,
186
227
_ => return Err ( Error :: InvalidNameType ) ,
187
228
} )
188
229
}
189
230
190
231
fn tag ( & self ) -> u64 {
191
232
// Defined in the GeneralName list in
192
233
// https://tools.ietf.org/html/rfc5280#page-38
234
+ const TAG_OTHER_NAME : u64 = 0 ;
193
235
const TAG_RFC822_NAME : u64 = 1 ;
194
236
const TAG_DNS_NAME : u64 = 2 ;
195
237
const TAG_URI : u64 = 6 ;
196
238
const TAG_IP_ADDRESS : u64 = 7 ;
197
239
198
240
match self {
241
+ Self :: OtherName ( _oid) => TAG_OTHER_NAME ,
199
242
SanType :: Rfc822Name ( _name) => TAG_RFC822_NAME ,
200
243
SanType :: DnsName ( _name) => TAG_DNS_NAME ,
201
244
SanType :: URI ( _name) => TAG_URI ,
@@ -865,6 +908,22 @@ impl CertificateParams {
865
908
SanType :: IpAddress ( IpAddr :: V6 ( addr) ) => {
866
909
writer. write_bytes ( & addr. octets ( ) )
867
910
} ,
911
+ SanType :: OtherName ( ( oid, value) ) => {
912
+ // otherName SEQUENCE { OID, [0] explicit any defined by oid }
913
+ // https://datatracker.ietf.org/doc/html/rfc5280#page-38
914
+ let oid = ObjectIdentifier :: from_slice ( & oid) ;
915
+ writer. write_sequence ( |writer| {
916
+ writer. next ( ) . write_oid ( & oid) ;
917
+ writer. next ( ) . write_tagged (
918
+ Tag :: context ( 0 ) ,
919
+ |writer| match value {
920
+ OtherNameValue :: Utf8String ( s) => {
921
+ writer. write_utf8_string ( s)
922
+ } ,
923
+ } ,
924
+ ) ;
925
+ } ) ;
926
+ } ,
868
927
} ,
869
928
) ;
870
929
}
0 commit comments