x509_certificate/
rfc3280.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! ASN.1 types defined in RFC 3280.
6
7use {
8    crate::rfc4519::{
9        OID_COMMON_NAME, OID_COUNTRY_NAME, OID_LOCALITY_NAME, OID_ORGANIZATIONAL_UNIT_NAME,
10        OID_ORGANIZATION_NAME, OID_STATE_PROVINCE_NAME,
11    },
12    bcder::{
13        decode::{BytesSource, Constructed, DecodeError, Source},
14        encode,
15        encode::{PrimitiveContent, Values},
16        string::{Ia5String, PrintableString, Utf8String},
17        Captured, Mode, OctetString, Oid, Tag,
18    },
19    std::{
20        fmt::{Debug, Display, Formatter},
21        io::Write,
22        ops::{Deref, DerefMut},
23        str::FromStr,
24    },
25};
26
27pub type GeneralNames = Vec<GeneralName>;
28
29/// General name.
30///
31/// ```ASN.1
32/// GeneralName ::= CHOICE {
33///   otherName                       [0]     AnotherName,
34///   rfc822Name                      [1]     IA5String,
35///   dNSName                         [2]     IA5String,
36///   x400Address                     [3]     ORAddress,
37///   directoryName                   [4]     Name,
38///   ediPartyName                    [5]     EDIPartyName,
39///   uniformResourceIdentifier       [6]     IA5String,
40///   iPAddress                       [7]     OCTET STRING,
41///   registeredID                    [8]     OBJECT IDENTIFIER }
42/// ```
43#[derive(Clone, Debug, Eq, PartialEq)]
44pub enum GeneralName {
45    OtherName(AnotherName),
46    Rfc822Name(Ia5String),
47    DnsName(Ia5String),
48    X400Address(OrAddress),
49    DirectoryName(Name),
50    EdiPartyName(EdiPartyName),
51    UniformResourceIdentifier(Ia5String),
52    IpAddress(OctetString),
53    RegisteredId(Oid),
54}
55
56impl GeneralName {
57    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
58        match cons.take_opt_constructed_if(Tag::CTX_0, |cons| AnotherName::take_from(cons))?
59        { Some(name) => {
60            Ok(Self::OtherName(name))
61        } _ => { match cons.take_opt_constructed_if(Tag::CTX_1, |cons| Ia5String::take_from(cons))?
62        { Some(name) => {
63            Ok(Self::Rfc822Name(name))
64        } _ => { match cons.take_opt_constructed_if(Tag::CTX_2, |cons| Ia5String::take_from(cons))?
65        { Some(name) => {
66            Ok(Self::DnsName(name))
67        } _ => if let Some(name) =
68            cons.take_opt_constructed_if(Tag::CTX_3, |cons| OrAddress::take_from(cons))?
69        {
70            Ok(Self::X400Address(name))
71        } else { match cons.take_opt_constructed_if(Tag::CTX_4, |cons| Name::take_from(cons))?
72        { Some(name) => {
73            Ok(Self::DirectoryName(name))
74        } _ => { match cons.take_opt_constructed_if(Tag::CTX_5, |cons| EdiPartyName::take_from(cons))?
75        { Some(name) => {
76            Ok(Self::EdiPartyName(name))
77        } _ => { match cons.take_opt_constructed_if(Tag::CTX_6, |cons| Ia5String::take_from(cons))?
78        { Some(name) => {
79            Ok(Self::UniformResourceIdentifier(name))
80        } _ => { match cons.take_opt_constructed_if(Tag::ctx(7), |cons| OctetString::take_from(cons))?
81        { Some(name) => {
82            Ok(Self::IpAddress(name))
83        } _ => { match cons.take_opt_constructed_if(Tag::ctx(8), |cons| Oid::take_from(cons))?
84        { Some(name) => {
85            Ok(Self::RegisteredId(name))
86        } _ => {
87            Err(cons.content_err("unexpected GeneralName variant"))
88        }}}}}}}}}}}}}}}}
89    }
90
91    pub fn encode_ref(&self) -> impl Values + '_ {
92        match self {
93            Self::OtherName(name) => (
94                Some(name.explicit(Tag::CTX_0)),
95                None,
96                None,
97                None,
98                None,
99                None,
100                None,
101                None,
102            ),
103            Self::Rfc822Name(name) => (
104                None,
105                Some(name.encode_ref_as(Tag::CTX_1)),
106                None,
107                None,
108                None,
109                None,
110                None,
111                None,
112            ),
113            Self::DnsName(name) => (
114                None,
115                None,
116                Some(name.encode_ref_as(Tag::CTX_2)),
117                None,
118                None,
119                None,
120                None,
121                None,
122            ),
123            Self::X400Address(_name) => {
124                unimplemented!()
125            }
126            Self::DirectoryName(name) => (
127                None,
128                None,
129                None,
130                Some(name.encode_ref_as(Tag::CTX_4)),
131                None,
132                None,
133                None,
134                None,
135            ),
136            Self::EdiPartyName(name) => (
137                None,
138                None,
139                None,
140                None,
141                Some(name.encode_ref_as(Tag::CTX_5)),
142                None,
143                None,
144                None,
145            ),
146            Self::UniformResourceIdentifier(name) => (
147                None,
148                None,
149                None,
150                None,
151                None,
152                Some(name.encode_ref_as(Tag::CTX_6)),
153                None,
154                None,
155            ),
156            Self::IpAddress(name) => (
157                None,
158                None,
159                None,
160                None,
161                None,
162                None,
163                Some(name.encode_ref_as(Tag::ctx(7))),
164                None,
165            ),
166            Self::RegisteredId(name) => (
167                None,
168                None,
169                None,
170                None,
171                None,
172                None,
173                None,
174                Some(name.encode_ref_as(Tag::ctx(8))),
175            ),
176        }
177    }
178}
179
180/// A reference to another name.
181///
182/// ```ASN.1
183/// AnotherName ::= SEQUENCE {
184///   type-id    OBJECT IDENTIFIER,
185///   value      [0] EXPLICIT ANY DEFINED BY type-id }
186/// ```
187#[derive(Clone, Debug)]
188pub struct AnotherName {
189    pub type_id: Oid,
190    pub value: Captured,
191}
192
193impl PartialEq for AnotherName {
194    fn eq(&self, other: &Self) -> bool {
195        self.type_id == other.type_id && self.value.as_slice() == other.value.as_slice()
196    }
197}
198
199impl Eq for AnotherName {}
200
201impl AnotherName {
202    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
203        cons.take_sequence(|cons| {
204            let type_id = Oid::take_from(cons)?;
205            let value = cons.take_constructed_if(Tag::CTX_0, |cons| cons.capture_all())?;
206
207            Ok(Self { type_id, value })
208        })
209    }
210}
211
212impl Values for AnotherName {
213    fn encoded_len(&self, mode: Mode) -> usize {
214        encode::sequence((self.type_id.encode_ref(), &self.value)).encoded_len(mode)
215    }
216
217    fn write_encoded<W: Write>(&self, mode: Mode, target: &mut W) -> Result<(), std::io::Error> {
218        encode::sequence((self.type_id.encode_ref(), &self.value)).write_encoded(mode, target)
219    }
220}
221
222/// EDI party name.
223///
224/// ```ASN.1
225/// EDIPartyName ::= SEQUENCE {
226///   nameAssigner            [0]     DirectoryString OPTIONAL,
227///   partyName               [1]     DirectoryString }
228/// ```
229#[derive(Clone, Debug, Eq, PartialEq)]
230pub struct EdiPartyName {
231    pub name_assigner: Option<DirectoryString>,
232    pub party_name: DirectoryString,
233}
234
235impl EdiPartyName {
236    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
237        cons.take_sequence(|cons| {
238            let name_assigner =
239                cons.take_opt_constructed_if(Tag::CTX_0, |cons| DirectoryString::take_from(cons))?;
240            let party_name =
241                cons.take_constructed_if(Tag::CTX_1, |cons| DirectoryString::take_from(cons))?;
242
243            Ok(Self {
244                name_assigner,
245                party_name,
246            })
247        })
248    }
249
250    pub fn encode_ref(&self) -> impl Values + '_ {
251        encode::sequence((
252            self.name_assigner
253                .as_ref()
254                .map(|name_assigner| name_assigner.encode_ref()),
255            self.party_name.encode_ref(),
256        ))
257    }
258
259    pub fn encode_ref_as(&self, tag: Tag) -> impl Values + '_ {
260        encode::sequence_as(
261            tag,
262            (
263                self.name_assigner
264                    .as_ref()
265                    .map(|name_assigner| name_assigner.encode_ref()),
266                self.party_name.encode_ref(),
267            ),
268        )
269    }
270}
271
272/// Directory string.
273///
274/// ```ASN.1
275/// DirectoryString ::= CHOICE {
276///       teletexString           TeletexString (SIZE (1..MAX)),
277///       printableString         PrintableString (SIZE (1..MAX)),
278///       universalString         UniversalString (SIZE (1..MAX)),
279///       utf8String              UTF8String (SIZE (1..MAX)),
280///       bmpString               BMPString (SIZE (1..MAX)) }
281/// ```
282#[derive(Clone, Debug, Eq, PartialEq)]
283pub enum DirectoryString {
284    // TODO implement.
285    TeletexString,
286    PrintableString(PrintableString),
287    // TODO implement.
288    UniversalString,
289    Utf8String(Utf8String),
290    // TODO implement.
291    BmpString,
292}
293
294impl DirectoryString {
295    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
296        cons.take_value(|tag, content| {
297            if tag == Tag::PRINTABLE_STRING {
298                Ok(Self::PrintableString(PrintableString::from_content(
299                    content,
300                )?))
301            } else if tag == Tag::UTF8_STRING {
302                Ok(Self::Utf8String(Utf8String::from_content(content)?))
303            } else {
304                Err(content
305                    .content_err("only decoding of PrintableString and UTF8String is implemented"))
306            }
307        })
308    }
309
310    pub fn encode_ref(&self) -> impl Values + '_ {
311        match self {
312            Self::PrintableString(ps) => (Some(ps.encode_ref()), None),
313            Self::Utf8String(s) => (None, Some(s.encode_ref())),
314            _ => unimplemented!(),
315        }
316    }
317}
318
319impl Display for DirectoryString {
320    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
321        let str = match self {
322            Self::PrintableString(s) => s.to_string(),
323            Self::Utf8String(s) => s.to_string(),
324            _ => unimplemented!(),
325        };
326        write!(f, "{}", str)
327    }
328}
329
330impl Values for DirectoryString {
331    fn encoded_len(&self, mode: Mode) -> usize {
332        self.encode_ref().encoded_len(mode)
333    }
334
335    fn write_encoded<W: Write>(&self, mode: Mode, target: &mut W) -> Result<(), std::io::Error> {
336        self.encode_ref().write_encoded(mode, target)
337    }
338}
339
340#[derive(Clone, Debug, Eq, PartialEq)]
341pub enum Name {
342    RdnSequence(RdnSequence),
343}
344
345impl Name {
346    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
347        Ok(Self::RdnSequence(RdnSequence::take_from(cons)?))
348    }
349
350    pub fn encode_ref(&self) -> impl Values + '_ {
351        match self {
352            Self::RdnSequence(seq) => seq.encode_ref(),
353        }
354    }
355
356    pub fn encode_ref_as(&self, tag: Tag) -> impl Values + '_ {
357        match self {
358            Self::RdnSequence(seq) => seq.encode_ref_as(tag),
359        }
360    }
361
362    /// Iterate over relative distinguished name entries in this instance.
363    pub fn iter_rdn(&self) -> impl Iterator<Item = &RelativeDistinguishedName> {
364        self.0.iter()
365    }
366
367    /// Iterate over all attributes in this Name.
368    pub fn iter_attributes(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
369        self.0.iter().flat_map(|rdn| rdn.iter())
370    }
371
372    /// Iterate over all attributes, yielding mutable entries.
373    pub fn iter_mut_attributes(&mut self) -> impl Iterator<Item = &mut AttributeTypeAndValue> {
374        self.0.iter_mut().flat_map(|rdn| rdn.iter_mut())
375    }
376
377    /// Iterate over all attributes in this Name having a given OID.
378    pub fn iter_by_oid(&self, oid: Oid) -> impl Iterator<Item = &AttributeTypeAndValue> {
379        self.iter_attributes().filter(move |atv| atv.typ == oid)
380    }
381
382    /// Iterate over all attributes in this Name having a given OID, yielding mutable instances.
383    pub fn iter_mut_by_oid(
384        &mut self,
385        oid: Oid,
386    ) -> impl Iterator<Item = &mut AttributeTypeAndValue> {
387        self.iter_mut_attributes().filter(move |atv| atv.typ == oid)
388    }
389
390    /// Iterate over all Common Name (CN) attributes.
391    pub fn iter_common_name(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
392        self.iter_by_oid(Oid(OID_COMMON_NAME.as_ref().into()))
393    }
394
395    /// Iterate over all Country (C) attributes.
396    pub fn iter_country(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
397        self.iter_by_oid(Oid(OID_COUNTRY_NAME.as_ref().into()))
398    }
399
400    /// Iterate over all Locality Name (L) attributes.
401    pub fn iter_locality(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
402        self.iter_by_oid(Oid(OID_LOCALITY_NAME.as_ref().into()))
403    }
404
405    /// Iterate over all State or Province attributes.
406    pub fn iter_state_province(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
407        self.iter_by_oid(Oid(OID_STATE_PROVINCE_NAME.as_ref().into()))
408    }
409
410    /// Iterate over all Organization (O) attributes.
411    pub fn iter_organization(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
412        self.iter_by_oid(Oid(OID_ORGANIZATION_NAME.as_ref().into()))
413    }
414
415    /// Iterate over all Organizational Unit (OU) attributes.
416    pub fn iter_organizational_unit(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
417        self.iter_by_oid(Oid(OID_ORGANIZATIONAL_UNIT_NAME.as_ref().into()))
418    }
419
420    /// Find the first attribute given an OID search.
421    pub fn find_attribute(&self, oid: Oid) -> Option<&AttributeTypeAndValue> {
422        self.iter_by_oid(oid).next()
423    }
424
425    /// Attempt to obtain a string attribute given
426    pub fn find_first_attribute_string(
427        &self,
428        oid: Oid,
429    ) -> Result<Option<String>, DecodeError<<BytesSource as Source>::Error>> {
430        if let Some(atv) = self.find_attribute(oid) {
431            Ok(Some(atv.to_string()?))
432        } else {
433            Ok(None)
434        }
435    }
436
437    /// Obtain a user friendly string representation of this instance.
438    ///
439    /// This prints common OIDs like common name and organization unit in a way
440    /// that is similar to how tools like OpenSSL render certificates.
441    ///
442    /// Not all attributes are printed. Do not compare the output of this
443    /// function to test equality.
444    pub fn user_friendly_str(&self) -> Result<String, DecodeError<<BytesSource as Source>::Error>> {
445        let mut fields = vec![];
446
447        for cn in self.iter_common_name() {
448            fields.push(format!("CN={}", cn.to_string()?));
449        }
450        for ou in self.iter_organizational_unit() {
451            fields.push(format!("OU={}", ou.to_string()?));
452        }
453        for o in self.iter_organization() {
454            fields.push(format!("O={}", o.to_string()?));
455        }
456        for l in self.iter_locality() {
457            fields.push(format!("L={}", l.to_string()?));
458        }
459        for state in self.iter_state_province() {
460            fields.push(format!("S={}", state.to_string()?));
461        }
462        for c in self.iter_country() {
463            fields.push(format!("C={}", c.to_string()?));
464        }
465
466        Ok(fields.join(", "))
467    }
468
469    /// Appends a PrintableString value for the given OID.
470    ///
471    /// The attribute will always be written to a new RDN.
472    pub fn append_printable_string(
473        &mut self,
474        oid: Oid,
475        value: &str,
476    ) -> Result<(), bcder::string::CharSetError> {
477        let mut rdn = RelativeDistinguishedName::default();
478        rdn.push(AttributeTypeAndValue::new_printable_string(oid, value)?);
479        self.0.push(rdn);
480
481        Ok(())
482    }
483
484    /// Appends a Utf8String value for the given OID.
485    ///
486    /// The attribute will always be written to a new RD.
487    pub fn append_utf8_string(
488        &mut self,
489        oid: Oid,
490        value: &str,
491    ) -> Result<(), bcder::string::CharSetError> {
492        let mut rdn = RelativeDistinguishedName::default();
493        rdn.push(AttributeTypeAndValue::new_utf8_string(oid, value)?);
494        self.0.push(rdn);
495
496        Ok(())
497    }
498
499    /// Append a Common Name (CN) attribute to the first RDN.
500    pub fn append_common_name_utf8_string(
501        &mut self,
502        value: &str,
503    ) -> Result<(), bcder::string::CharSetError> {
504        self.append_utf8_string(Oid(OID_COMMON_NAME.as_ref().into()), value)
505    }
506
507    /// Append a Country (C) attribute to the first RDN.
508    pub fn append_country_utf8_string(
509        &mut self,
510        value: &str,
511    ) -> Result<(), bcder::string::CharSetError> {
512        self.append_utf8_string(Oid(OID_COUNTRY_NAME.as_ref().into()), value)
513    }
514
515    /// Append an Organization Name (O) attribute to the first RDN.
516    pub fn append_organization_utf8_string(
517        &mut self,
518        value: &str,
519    ) -> Result<(), bcder::string::CharSetError> {
520        self.append_utf8_string(Oid(OID_ORGANIZATION_NAME.as_ref().into()), value)
521    }
522
523    /// Append an Organizational Unit (OU) attribute to the first RDN.
524    pub fn append_organizational_unit_utf8_string(
525        &mut self,
526        value: &str,
527    ) -> Result<(), bcder::string::CharSetError> {
528        self.append_utf8_string(Oid(OID_ORGANIZATIONAL_UNIT_NAME.as_ref().into()), value)
529    }
530}
531
532impl Default for Name {
533    fn default() -> Self {
534        Self::RdnSequence(RdnSequence::default())
535    }
536}
537
538impl Deref for Name {
539    type Target = RdnSequence;
540
541    fn deref(&self) -> &Self::Target {
542        match self {
543            Self::RdnSequence(seq) => seq,
544        }
545    }
546}
547
548impl DerefMut for Name {
549    fn deref_mut(&mut self) -> &mut Self::Target {
550        match self {
551            Self::RdnSequence(seq) => seq,
552        }
553    }
554}
555
556#[derive(Clone, Debug, Default, Eq, PartialEq)]
557pub struct RdnSequence(Vec<RelativeDistinguishedName>);
558
559impl Deref for RdnSequence {
560    type Target = Vec<RelativeDistinguishedName>;
561
562    fn deref(&self) -> &Self::Target {
563        &self.0
564    }
565}
566
567impl DerefMut for RdnSequence {
568    fn deref_mut(&mut self) -> &mut Self::Target {
569        &mut self.0
570    }
571}
572
573impl RdnSequence {
574    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
575        cons.take_sequence(|cons| {
576            let mut values = Vec::new();
577
578            while let Some(value) = RelativeDistinguishedName::take_opt_from(cons)? {
579                values.push(value);
580            }
581
582            Ok(Self(values))
583        })
584    }
585
586    pub fn encode_ref(&self) -> impl Values + '_ {
587        encode::sequence(&self.0)
588    }
589
590    pub fn encode_ref_as(&self, tag: Tag) -> impl Values + '_ {
591        encode::sequence_as(tag, &self.0)
592    }
593}
594
595pub type DistinguishedName = RdnSequence;
596
597/// Relative distinguished name.
598///
599/// ```ASN.1
600/// RelativeDistinguishedName ::=
601///   SET OF AttributeTypeAndValue
602/// ```
603#[derive(Clone, Debug, Default, Eq, PartialEq)]
604pub struct RelativeDistinguishedName(Vec<AttributeTypeAndValue>);
605
606impl Deref for RelativeDistinguishedName {
607    type Target = Vec<AttributeTypeAndValue>;
608
609    fn deref(&self) -> &Self::Target {
610        &self.0
611    }
612}
613
614impl DerefMut for RelativeDistinguishedName {
615    fn deref_mut(&mut self) -> &mut Self::Target {
616        &mut self.0
617    }
618}
619
620impl RelativeDistinguishedName {
621    pub fn take_opt_from<S: Source>(
622        cons: &mut Constructed<S>,
623    ) -> Result<Option<Self>, DecodeError<S::Error>> {
624        cons.take_opt_set(|cons| {
625            let mut values = Vec::new();
626
627            while let Some(value) = AttributeTypeAndValue::take_opt_from(cons)? {
628                values.push(value);
629            }
630
631            Ok(Self(values))
632        })
633    }
634
635    pub fn encode_ref(&self) -> impl Values + '_ {
636        encode::set(&self.0)
637    }
638}
639
640impl Values for RelativeDistinguishedName {
641    fn encoded_len(&self, mode: Mode) -> usize {
642        self.encode_ref().encoded_len(mode)
643    }
644
645    fn write_encoded<W: Write>(&self, mode: Mode, target: &mut W) -> Result<(), std::io::Error> {
646        self.encode_ref().write_encoded(mode, target)
647    }
648}
649
650#[derive(Clone, Debug, Eq, PartialEq)]
651pub struct OrAddress {}
652
653impl OrAddress {
654    pub fn take_from<S: Source>(cons: &mut Constructed<S>) -> Result<Self, DecodeError<S::Error>> {
655        Err(cons.content_err("parsing of OrAddress not implemented"))
656    }
657}
658
659/// Attribute type and its value.
660///
661/// ```ASN.1
662/// AttributeTypeAndValue ::= SEQUENCE {
663///   type     AttributeType,
664///   value    AttributeValue }
665/// ```
666#[derive(Clone)]
667pub struct AttributeTypeAndValue {
668    pub typ: AttributeType,
669    pub value: AttributeValue,
670}
671
672impl Debug for AttributeTypeAndValue {
673    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
674        let mut s = f.debug_struct("AttributeTypeAndValue");
675        s.field("type", &format_args!("{}", self.typ));
676        s.field("value", &self.value);
677        s.finish()
678    }
679}
680
681impl AttributeTypeAndValue {
682    pub fn take_opt_from<S: Source>(
683        cons: &mut Constructed<S>,
684    ) -> Result<Option<Self>, DecodeError<S::Error>> {
685        cons.take_opt_sequence(|cons| {
686            let typ = AttributeType::take_from(cons)?;
687            let value = cons.capture_all()?;
688
689            Ok(Self {
690                typ,
691                value: value.into(),
692            })
693        })
694    }
695
696    pub fn encode_ref(&self) -> impl Values + '_ {
697        encode::sequence((self.typ.encode_ref(), self.value.deref()))
698    }
699
700    /// Attempt to coerce the stored value to a Rust string.
701    pub fn to_string(&self) -> Result<String, DecodeError<<BytesSource as Source>::Error>> {
702        self.value.to_string()
703    }
704
705    /// Construct a new instance with a PrintableString given an OID and Rust string.
706    pub fn new_printable_string(oid: Oid, s: &str) -> Result<Self, bcder::string::CharSetError> {
707        Ok(Self {
708            typ: oid,
709            value: AttributeValue::new_printable_string(s)?,
710        })
711    }
712
713    /// Construct a new instance with a Utf8String given an OID and Rust string.
714    pub fn new_utf8_string(oid: Oid, s: &str) -> Result<Self, bcder::string::CharSetError> {
715        Ok(Self {
716            typ: oid,
717            value: AttributeValue::new_utf8_string(s)?,
718        })
719    }
720
721    /// Set the captured value to a Utf8String.
722    pub fn set_utf8_string_value(&mut self, s: &str) -> Result<(), bcder::string::CharSetError> {
723        self.value.set_utf8_string_value(s)
724    }
725}
726
727impl PartialEq for AttributeTypeAndValue {
728    fn eq(&self, other: &Self) -> bool {
729        self.typ == other.typ && self.value.as_slice() == other.value.as_slice()
730    }
731}
732
733impl Eq for AttributeTypeAndValue {}
734
735impl Values for AttributeTypeAndValue {
736    fn encoded_len(&self, mode: Mode) -> usize {
737        self.encode_ref().encoded_len(mode)
738    }
739
740    fn write_encoded<W: Write>(&self, mode: Mode, target: &mut W) -> Result<(), std::io::Error> {
741        self.encode_ref().write_encoded(mode, target)
742    }
743}
744
745pub type AttributeType = Oid;
746
747#[derive(Clone)]
748pub struct AttributeValue(Captured);
749
750impl Debug for AttributeValue {
751    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
752        f.write_fmt(format_args!("{}", hex::encode(self.0.as_slice())))
753    }
754}
755
756impl AttributeValue {
757    /// Construct a new instance containing a PrintableString given a Rust string.
758    pub fn new_printable_string(s: &str) -> Result<Self, bcder::string::CharSetError> {
759        let mut slf = Self(Captured::empty(Mode::Der));
760
761        slf.set_printable_string_value(s)?;
762
763        Ok(slf)
764    }
765
766    /// Construct a new instance containing a Utf8String given a Rust string.
767    pub fn new_utf8_string(s: &str) -> Result<Self, bcder::string::CharSetError> {
768        let mut slf = Self(Captured::empty(Mode::Der));
769
770        slf.set_utf8_string_value(s)?;
771
772        Ok(slf)
773    }
774
775    /// Attempt to convert the inner value to a Rust string.
776    ///
777    /// The inner value can be any number of different types. This will try
778    /// a lot of them and hopefully yield a working result.
779    ///
780    /// If the inner type isn't a known string, a decoding error occurs.
781    pub fn to_string(&self) -> Result<String, DecodeError<<BytesSource as Source>::Error>> {
782        self.0.clone().decode(|cons| {
783            match cons.take_opt_value_if(Tag::NUMERIC_STRING, |content| {
784                bcder::NumericString::from_content(content)
785            })? { Some(s) => {
786                Ok(s.to_string())
787            } _ => { match cons.take_opt_value_if(Tag::PRINTABLE_STRING, |content| {
788                bcder::PrintableString::from_content(content)
789            })? { Some(s) => {
790                Ok(s.to_string())
791            } _ => { match cons.take_opt_value_if(Tag::UTF8_STRING, |content| {
792                bcder::Utf8String::from_content(content)
793            })? { Some(s) => {
794                Ok(s.to_string())
795            } _ => { match cons.take_opt_value_if(Tag::IA5_STRING, |content| {
796                bcder::Ia5String::from_content(content)
797            })? { Some(s) => {
798                Ok(s.to_string())
799            } _ => {
800                Ok(DirectoryString::take_from(cons)?.to_string())
801            }}}}}}}}
802        })
803    }
804
805    /// Set the captured value to a PrintableString.
806    pub fn set_printable_string_value(
807        &mut self,
808        value: &str,
809    ) -> Result<(), bcder::string::CharSetError> {
810        let ps = DirectoryString::PrintableString(PrintableString::from_str(value)?);
811        let captured = bcder::Captured::from_values(Mode::Der, ps);
812        self.0 = captured;
813
814        Ok(())
815    }
816
817    /// Set the captured value to a Utf8String.
818    pub fn set_utf8_string_value(
819        &mut self,
820        value: &str,
821    ) -> Result<(), bcder::string::CharSetError> {
822        let ds = DirectoryString::Utf8String(Utf8String::from_str(value)?);
823        let captured = bcder::Captured::from_values(Mode::Der, ds);
824        self.0 = captured;
825
826        Ok(())
827    }
828}
829
830impl Deref for AttributeValue {
831    type Target = Captured;
832
833    fn deref(&self) -> &Self::Target {
834        &self.0
835    }
836}
837
838impl From<Captured> for AttributeValue {
839    fn from(v: Captured) -> Self {
840        Self(v)
841    }
842}