Skip to main content

polyproto/certs/capabilities/
mod.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/// "basicConstraints" IdCert/Csr capabilities
6pub mod basic_constraints;
7/// "keyUsage" IdCert/Csr capabilities
8pub mod key_usage;
9
10pub use basic_constraints::*;
11pub use key_usage::*;
12use x509_cert::{
13    attr::{Attribute, Attributes},
14    ext::{Extension, Extensions},
15};
16
17use crate::errors::{CertificateConversionError, InvalidInput};
18
19/// Object Identifier for the KeyUsage::DigitalSignature variant.
20pub const OID_KEY_USAGE_DIGITAL_SIGNATURE: &str = "1.3.6.1.5.5.7.3.3";
21/// Object Identifier for the KeyUsage::CrlSign variant.
22pub const OID_KEY_USAGE_CRL_SIGN: &str = "1.3.6.1.5.5.7.3.2";
23/// Object Identifier for the KeyUsage::ContentCommitment variant.
24pub const OID_KEY_USAGE_CONTENT_COMMITMENT: &str = "1.3.6.1.5.5.7.3.8";
25/// Object Identifier for the KeyUsage::KeyEncipherment variant.
26pub const OID_KEY_USAGE_KEY_ENCIPHERMENT: &str = "1.3.6.1.5.5.7.3.1";
27/// Object Identifier for the KeyUsage::DataEncipherment variant.
28pub const OID_KEY_USAGE_DATA_ENCIPHERMENT: &str = "1.3.6.1.5.5.7.3.4";
29/// Object Identifier for the KeyUsage::KeyAgreement variant.
30pub const OID_KEY_USAGE_KEY_AGREEMENT: &str = "1.3.6.1.5.5.7.3.9";
31/// Object Identifier for the KeyUsage::KeyCertSign variant.
32pub const OID_KEY_USAGE_KEY_CERT_SIGN: &str = "1.3.6.1.5.5.7.3.3";
33/// Object Identifier for the KeyUsage::EncipherOnly variant.
34pub const OID_KEY_USAGE_ENCIPHER_ONLY: &str = "1.3.6.1.5.5.7.3.7";
35/// Object Identifier for the KeyUsage::DecipherOnly variant.
36pub const OID_KEY_USAGE_DECIPHER_ONLY: &str = "1.3.6.1.5.5.7.3.6";
37/// Object Identifier for the BasicConstraints variant.
38pub const OID_BASIC_CONSTRAINTS: &str = "2.5.29.19";
39/// Object Identifier for the KeyUsage flag.
40pub const OID_KEY_USAGE: &str = "2.5.29.15";
41
42#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
43/// An abstraction over X.509 Extensions and PKCS#10 Attributes, representing
44/// the capabilities of a certificate. Capabilities can be converted from and to
45/// both [Attributes] and [Extensions].
46///
47/// This struct only covers the Attributes/Extensions currently relevant to
48/// polyproto.
49pub struct Capabilities {
50    /// The key usage extension defines the purpose of the key contained in the
51    /// certificate.
52    pub key_usage: KeyUsages,
53    /// Extension type that defines whether a given certificate is allowed
54    /// to sign additional certificates and what path length restrictions may
55    /// exist.
56    pub basic_constraints: BasicConstraints,
57}
58
59impl Default for Capabilities {
60    fn default() -> Self {
61        Self {
62            key_usage: Default::default(),
63            basic_constraints: BasicConstraints { ca: false, path_length: None },
64        }
65    }
66}
67
68impl Capabilities {
69    /// Sane default for actor ID-CSR/ID-Cert [`Capabilities`]. Uses the
70    /// `DigitalSignature` flag, not the `ContentCommitment` flag.
71    #[must_use]
72    pub fn default_actor() -> Self {
73        let key_usage = KeyUsages::new(&[KeyUsage::DigitalSignature]);
74        let basic_constraints = BasicConstraints { ca: false, path_length: None };
75        Self { key_usage, basic_constraints }
76    }
77
78    /// Sane default for home server ID-CSR/ID-Cert [`Capabilities`].
79    #[must_use]
80    pub fn default_home_server() -> Self {
81        let key_usage = KeyUsages::new(&[KeyUsage::KeyCertSign]);
82        let basic_constraints = BasicConstraints { ca: true, path_length: Some(1) };
83        Self { key_usage, basic_constraints }
84    }
85
86    /// Validates that these capabilities are well-formed according to X.509 and
87    /// polyproto constraints. This validation is target-independent.
88    ///
89    /// ## Errors
90    ///
91    /// Returns a [crate::errors::ConstraintError] if the capabilities are
92    /// malformed.
93    pub fn validate(&self) -> Result<(), crate::errors::ConstraintError> {
94        let is_ca = self.basic_constraints.ca;
95
96        let mut can_commit_content = false;
97        let mut can_sign = false;
98        let mut key_cert_sign = false;
99        let mut has_only_encipher = false;
100        let mut has_only_decipher = false;
101        let mut has_key_agreement = false;
102
103        for item in self.key_usage.key_usages.iter() {
104            if !has_only_encipher && item == &KeyUsage::EncipherOnly {
105                has_only_encipher = true;
106            }
107            if !has_only_decipher && item == &KeyUsage::DecipherOnly {
108                has_only_decipher = true;
109            }
110            if !has_key_agreement && item == &KeyUsage::KeyAgreement {
111                has_key_agreement = true;
112            }
113            if item == &KeyUsage::ContentCommitment {
114                can_commit_content = true;
115            }
116            if item == &KeyUsage::DigitalSignature {
117                can_sign = true;
118            }
119            if item == &KeyUsage::KeyCertSign {
120                key_cert_sign = true;
121            }
122        }
123
124        // Non-CAs must be able to sign their messages
125        if !is_ca && !can_sign && !can_commit_content {
126            return Err(crate::errors::ConstraintError::Malformed(Some(
127                crate::errors::ERR_MSG_ACTOR_MISSING_SIGNING_CAPS.to_owned(),
128            )));
129        }
130
131        // Cannot have both signing and non-repudiation
132        if can_sign && can_commit_content {
133            return Err(crate::errors::ConstraintError::Malformed(Some(
134                "Cannot have both signing and non-repudiation signing capabilities".to_owned(),
135            )));
136        }
137
138        // CA <-> KeyCertSign correlation
139        if is_ca || key_cert_sign {
140            if !is_ca {
141                return Err(crate::errors::ConstraintError::Malformed(Some(
142                    "If KeyCertSign capability is wanted, CA flag must be true".to_owned(),
143                )));
144            }
145            if !key_cert_sign {
146                return Err(crate::errors::ConstraintError::Malformed(Some(format!(
147                    "{} Missing capability \"KeyCertSign\"",
148                    crate::errors::ERR_MSG_HOME_SERVER_MISSING_CA_ATTR
149                ))));
150            }
151        }
152
153        // KeyAgreement required for EncipherOnly/DecipherOnly
154        if (has_only_encipher || has_only_decipher) && !has_key_agreement {
155            Err(crate::errors::ConstraintError::Malformed(Some(
156                "KeyAgreement capability needs to be true to use OnlyEncipher or OnlyDecipher"
157                    .to_owned(),
158            )))
159        } else {
160            Ok(())
161        }
162    }
163
164    /// Validates that these capabilities are correct for an **actor**
165    /// certificate.
166    ///
167    /// In addition to the base validation, this checks:
168    /// - The `CA` flag must be `false`
169    /// - Must have `DigitalSignature` or `ContentCommitment`
170    /// - Must not have `KeyCertSign`
171    ///
172    /// ## Errors
173    ///
174    /// Returns a [crate::errors::ConstraintError] if the capabilities are
175    /// not valid for an actor.
176    pub fn validate_for_actor(&self) -> Result<(), crate::errors::ConstraintError> {
177        self.validate()?;
178        if self.basic_constraints.ca {
179            return Err(crate::errors::ConstraintError::Malformed(Some(
180                crate::errors::ERR_MSG_ACTOR_CANNOT_BE_CA.to_owned(),
181            )));
182        }
183        Ok(())
184    }
185
186    /// Validates that these capabilities are correct for a **home server**
187    /// certificate.
188    ///
189    /// In addition to the base validation, this checks:
190    /// - The `CA` flag must be `true`
191    /// - Must have `KeyCertSign`
192    ///
193    /// ## Errors
194    ///
195    /// Returns a [crate::errors::ConstraintError] if the capabilities are
196    /// not valid for a home server.
197    pub fn validate_for_home_server(&self) -> Result<(), crate::errors::ConstraintError> {
198        self.validate()?;
199        if !self.basic_constraints.ca {
200            return Err(crate::errors::ConstraintError::Malformed(Some(
201                crate::errors::ERR_MSG_HOME_SERVER_MISSING_CA_ATTR.to_owned(),
202            )));
203        }
204        Ok(())
205    }
206}
207
208impl TryFrom<Attributes> for Capabilities {
209    type Error = CertificateConversionError;
210
211    /// Performs the conversion.
212    ///
213    /// Fails if the [BasicConstraints] or [KeyUsage]s are malformed. The
214    /// constraints returned by this method are not guaranteed to be valid.
215    /// You should call `validate()` on the result to ensure that the
216    /// constraints are valid according to the X.509 standard and the polyproto
217    /// specification.
218    fn try_from(value: Attributes) -> Result<Self, Self::Error> {
219        let mut key_usages = KeyUsages::new(&[]);
220        let mut basic_constraints = BasicConstraints::default();
221        let mut num_basic_constraints = 0u8;
222        for item in value.iter() {
223            match item.oid.to_string().as_str() {
224                #[allow(unreachable_patterns)] // cargo thinks the below pattern is unreachable.
225                OID_KEY_USAGE => {
226                    key_usages = KeyUsages::try_from(item.clone())?;
227                }
228                OID_BASIC_CONSTRAINTS => {
229                    num_basic_constraints = num_basic_constraints.saturating_add(1);
230                    if num_basic_constraints > 1 {
231                        return Err(CertificateConversionError::InvalidInput(InvalidInput::Malformed("Tried inserting > 1 BasicConstraints into Capabilities. Expected 1 BasicConstraints".to_owned())));
232                    }
233                    basic_constraints = BasicConstraints::try_from(item.clone())?;
234                }
235                _ => (),
236            }
237        }
238        Ok(Self { key_usage: key_usages, basic_constraints })
239    }
240}
241
242impl TryFrom<Capabilities> for Attributes {
243    type Error = CertificateConversionError;
244
245    /// Performs the conversion.
246    ///
247    /// Fails, if `Capabilities::validate()` fails.
248    fn try_from(value: Capabilities) -> Result<Self, Self::Error> {
249        let mut sov = Self::new();
250        let insertion = sov.insert(Attribute::try_from(value.key_usage)?);
251        if insertion.is_err() {
252            return Err(CertificateConversionError::InvalidInput(InvalidInput::Malformed("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_owned())));
253        }
254        let insertion = sov.insert(Attribute::try_from(value.basic_constraints)?);
255        if insertion.is_err() {
256            return Err(CertificateConversionError::InvalidInput(InvalidInput::Malformed("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_owned())));
257        }
258        Ok(sov)
259    }
260}
261
262impl TryFrom<Capabilities> for Extensions {
263    type Error = CertificateConversionError;
264    /// Performs the conversion.
265    ///
266    /// try_from does **not** check whether the resulting [Extensions] are
267    /// well-formed.
268    fn try_from(value: Capabilities) -> Result<Self, Self::Error> {
269        // the order is important
270        Ok(vec![
271            Extension::try_from(value.basic_constraints)?,
272            Extension::try_from(value.key_usage)?,
273        ])
274    }
275}
276
277/// Capabilities for an **actor** certificate.
278///
279/// This newtype wraps [`Capabilities`] and guarantees that the contained
280/// capabilities satisfy all actor-specific constraints (non-CA, signing
281/// capability present). Use [`ActorCapabilities::try_new`] to construct one
282/// from arbitrary [`Capabilities`], or [`ActorCapabilities::default`] for
283/// the standard actor defaults.
284#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
285pub struct ActorCapabilities(pub(crate) Capabilities);
286
287impl ActorCapabilities {
288    /// Creates actor capabilities from key usages.
289    ///
290    /// CA flag is always `false` for actors, and `path_length` is always
291    /// `None`.
292    ///
293    /// ## Errors
294    ///
295    /// Returns a [`crate::errors::ConstraintError`] if the capabilities are
296    /// not valid for an actor (e.g. signing capability missing).
297    pub fn try_new(key_usages: KeyUsages) -> Result<Self, crate::errors::ConstraintError> {
298        let caps = Capabilities {
299            key_usage: key_usages,
300            basic_constraints: BasicConstraints { ca: false, path_length: None },
301        };
302        caps.validate_for_actor()?;
303        Ok(Self(caps))
304    }
305}
306
307impl Default for ActorCapabilities {
308    fn default() -> Self {
309        Self(Capabilities::default_actor())
310    }
311}
312
313impl std::ops::Deref for ActorCapabilities {
314    type Target = Capabilities;
315
316    fn deref(&self) -> &Self::Target {
317        &self.0
318    }
319}
320
321impl From<ActorCapabilities> for Capabilities {
322    fn from(value: ActorCapabilities) -> Self {
323        value.0
324    }
325}
326
327impl TryFrom<Capabilities> for ActorCapabilities {
328    type Error = crate::errors::ConstraintError;
329
330    fn try_from(value: Capabilities) -> Result<Self, Self::Error> {
331        Self::try_new(value.key_usage)
332    }
333}
334
335/// Capabilities for a **home server** certificate.
336///
337/// This newtype wraps [`Capabilities`] and guarantees that the contained
338/// capabilities satisfy all home-server-specific constraints (CA flag set,
339/// `KeyCertSign` present). Use [`HomeServerCapabilities::try_new`] to
340/// construct one from arbitrary [`Capabilities`], or
341/// [`HomeServerCapabilities::default`] for the standard home server defaults.
342#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
343pub struct HomeServerCapabilities(pub Capabilities);
344
345impl HomeServerCapabilities {
346    /// Creates home server capabilities from key usages and path length.
347    ///
348    /// CA flag is always `true` for home servers. The `path_length` parameter
349    /// controls the maximum certification path length.
350    ///
351    /// ## Errors
352    ///
353    /// Returns a [`crate::errors::ConstraintError`] if the capabilities are
354    /// not valid for a home server (e.g. `KeyCertSign` missing).
355    pub fn try_new(
356        key_usages: KeyUsages,
357        path_length: Option<u64>,
358    ) -> Result<Self, crate::errors::ConstraintError> {
359        let caps = Capabilities {
360            key_usage: key_usages,
361            basic_constraints: BasicConstraints { ca: true, path_length },
362        };
363        caps.validate_for_home_server()?;
364        Ok(Self(caps))
365    }
366}
367
368impl Default for HomeServerCapabilities {
369    fn default() -> Self {
370        Self(Capabilities::default_home_server())
371    }
372}
373
374impl std::ops::Deref for HomeServerCapabilities {
375    type Target = Capabilities;
376
377    fn deref(&self) -> &Self::Target {
378        &self.0
379    }
380}
381
382impl From<HomeServerCapabilities> for Capabilities {
383    fn from(value: HomeServerCapabilities) -> Self {
384        value.0
385    }
386}
387
388impl TryFrom<Capabilities> for HomeServerCapabilities {
389    type Error = crate::errors::ConstraintError;
390
391    fn try_from(value: Capabilities) -> Result<Self, Self::Error> {
392        Self::try_new(value.key_usage, value.basic_constraints.path_length)
393    }
394}
395
396impl TryFrom<Extensions> for Capabilities {
397    type Error = CertificateConversionError;
398
399    /// Performs the conversion.
400    ///
401    /// try_from does **not** check whether the resulting [Capabilities] are
402    /// well-formed. If this property is critical, use the
403    /// `Capabilities::validate()` method to verify the well-formedness of
404    /// these resulting [Capabilities].
405    fn try_from(value: Extensions) -> Result<Self, Self::Error> {
406        let mut basic_constraints: BasicConstraints = BasicConstraints::default();
407        let mut key_usage: KeyUsages = KeyUsages::default();
408        for item in value.iter() {
409            #[allow(unreachable_patterns)] // cargo thinks that we have an unreachable pattern here
410            match item.extn_id.to_string().as_str() {
411                OID_BASIC_CONSTRAINTS => {
412                    basic_constraints = BasicConstraints::try_from(item.clone())?
413                }
414                OID_KEY_USAGE => key_usage = KeyUsages::try_from(item.clone())?,
415                _ => {
416                    return Err(CertificateConversionError::InvalidInput(InvalidInput::Malformed(
417                        format!(
418                            "Invalid OID found for converting this set of Extensions to Capabilities: {} is not a valid OID for BasicConstraints or KeyUsages",
419                            item.extn_id
420                        ),
421                    )));
422                }
423            };
424        }
425        Ok(Capabilities { key_usage, basic_constraints })
426    }
427}
428
429#[cfg(test)]
430#[allow(clippy::unwrap_used)]
431mod test {
432    use spki::ObjectIdentifier;
433
434    use super::*;
435
436    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
437    #[cfg_attr(not(target_arch = "wasm32"), test)]
438    fn test_basic_constraints_to_object_identifier() {
439        let bc = BasicConstraints { ca: true, path_length: None };
440        let _ = ObjectIdentifier::from(bc);
441    }
442
443    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
444    #[cfg_attr(not(target_arch = "wasm32"), test)]
445    fn test_basic_constraints_to_attribute() {
446        let mut bc = BasicConstraints { ca: false, path_length: None };
447        let _ = Attribute::try_from(bc).expect("Should not fail in test");
448
449        bc.ca = true;
450        let _ = Attribute::try_from(bc).expect("Should not fail in test");
451
452        bc.path_length = Some(0);
453        let _ = Attribute::try_from(bc).expect("Should not fail in test");
454
455        // Why not test all sorts of values? :3
456        let mut county_count = 2u64;
457        while county_count != u64::MAX {
458            bc.path_length = Some(county_count);
459            let _ = Attribute::try_from(bc).expect("Should not fail in test");
460            if let Some(res) = county_count.checked_mul(2) {
461                county_count = res;
462            } else {
463                county_count = u64::MAX;
464            }
465        }
466    }
467}
468
469#[cfg(test)]
470#[allow(clippy::unwrap_used)]
471mod test_key_usage_from_attribute {
472    use std::str::FromStr;
473
474    use der::{Any, Tag, asn1::SetOfVec};
475    use spki::ObjectIdentifier;
476
477    use super::*;
478
479    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
480    #[cfg_attr(not(target_arch = "wasm32"), test)]
481    fn test_key_usage_tag_mismatch() {
482        let mut sov = SetOfVec::new();
483        sov.insert(Any::new(Tag::Integer, vec![0x00]).expect("Should not fail in test"))
484            .expect("Should not fail in test");
485        let attribute = Attribute {
486            oid: ObjectIdentifier::from_str(OID_KEY_USAGE_CONTENT_COMMITMENT)
487                .expect("Should not fail in test"),
488            values: sov,
489        };
490        let result = KeyUsages::try_from(attribute);
491        assert!(result.is_err());
492    }
493
494    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
495    #[cfg_attr(not(target_arch = "wasm32"), test)]
496    fn test_key_usage_wrong_oid() {
497        let mut sov = SetOfVec::new();
498        sov.insert(Any::new(Tag::Boolean, vec![0x00]).expect("Should not fail in test"))
499            .expect("Should not fail in test");
500        let attribute = Attribute {
501            oid: ObjectIdentifier::from_str("1.2.4.2.1.1.1.1.1.1.1.1.1.1.161.69")
502                .expect("Should not fail in test"),
503            values: sov,
504        };
505        let result = KeyUsages::try_from(attribute);
506        assert!(result.is_err());
507    }
508
509    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
510    #[cfg_attr(not(target_arch = "wasm32"), test)]
511    fn test_key_usage_from_attribute_weird_bool_value() {
512        let mut sov = SetOfVec::new();
513        sov.insert(Any::new(Tag::Boolean, vec![0x02]).expect("Should not fail in test"))
514            .expect("Should not fail in test");
515        let attribute = Attribute {
516            oid: ObjectIdentifier::from_str(OID_KEY_USAGE_CONTENT_COMMITMENT)
517                .expect("Should not fail in test"),
518            values: sov,
519        };
520        let result = KeyUsages::try_from(attribute);
521        assert!(result.is_err());
522    }
523}
524
525#[cfg(test)]
526#[allow(clippy::unwrap_used)]
527mod test_basic_constraints_from_attribute {
528    use std::str::FromStr;
529
530    use der::{
531        Any, Tag,
532        asn1::{Ia5String, SetOfVec},
533    };
534    use spki::ObjectIdentifier;
535
536    use super::*;
537
538    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
539    #[cfg_attr(not(target_arch = "wasm32"), test)]
540    fn test_basic_constraints_from_attribute() {
541        let bc = BasicConstraints { ca: true, path_length: Some(0) };
542        let attribute = Attribute::try_from(bc).expect("Should not fail in test");
543        let result = BasicConstraints::try_from(attribute);
544        assert!(result.is_ok());
545    }
546
547    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
548    #[cfg_attr(not(target_arch = "wasm32"), test)]
549    fn test_basic_constraints_wrong_value_type() {
550        let mut sov = SetOfVec::new();
551        sov.insert(
552            Any::new(
553                Tag::Ia5String,
554                Ia5String::new("hello").expect("Should not fail in test").as_bytes(),
555            )
556            .expect("Should not fail in test"),
557        )
558        .expect("Should not fail in test");
559        let attribute = Attribute {
560            oid: ObjectIdentifier::from_str(OID_BASIC_CONSTRAINTS)
561                .expect("Should not fail in test"),
562            values: sov,
563        };
564        let result = BasicConstraints::try_from(attribute);
565        assert!(result.is_err());
566    }
567
568    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
569    #[cfg_attr(not(target_arch = "wasm32"), test)]
570    fn test_basic_constraints_wrong_oid() {
571        let bc = BasicConstraints { ca: true, path_length: Some(0) };
572        let mut attribute = Attribute::try_from(bc).expect("Should not fail in test");
573        attribute.oid =
574            ObjectIdentifier::from_str("0.0.161.80085").expect("Should not fail in test");
575        let result = BasicConstraints::try_from(attribute);
576        assert!(result.is_err());
577    }
578
579    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
580    #[cfg_attr(not(target_arch = "wasm32"), test)]
581    fn test_basic_constraints_from_attribute_too_many_bools_or_ints() {
582        let mut sov = SetOfVec::new();
583        sov.insert(Any::new(Tag::Boolean, vec![0x00]).expect("Should not fail in test"))
584            .expect("Should not fail in test");
585        sov.insert(Any::new(Tag::Boolean, vec![0x01]).expect("Should not fail in test"))
586            .expect("Should not fail in test");
587        let attribute = Attribute {
588            oid: ObjectIdentifier::from_str(OID_BASIC_CONSTRAINTS)
589                .expect("Should not fail in test"),
590            values: sov,
591        };
592        let result = BasicConstraints::try_from(attribute);
593        assert!(result.is_err());
594
595        let mut sov = SetOfVec::new();
596        sov.insert(Any::new(Tag::Integer, vec![0x00]).expect("Should not fail in test"))
597            .expect("Should not fail in test");
598        sov.insert(Any::new(Tag::Integer, vec![0x01]).expect("Should not fail in test"))
599            .expect("Should not fail in test");
600        let attribute = Attribute {
601            oid: ObjectIdentifier::from_str(OID_BASIC_CONSTRAINTS)
602                .expect("Should not fail in test"),
603            values: sov,
604        };
605        let result = BasicConstraints::try_from(attribute);
606        assert!(result.is_err());
607    }
608
609    #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)]
610    #[cfg_attr(not(target_arch = "wasm32"), test)]
611    fn test_basic_constraints_from_attribute_weird_bool_value() {
612        let mut sov = SetOfVec::new();
613        sov.insert(Any::new(Tag::Boolean, vec![0x02]).expect("Should not fail in test"))
614            .expect("Should not fail in test");
615        let attribute = Attribute {
616            oid: ObjectIdentifier::from_str(OID_BASIC_CONSTRAINTS)
617                .expect("Should not fail in test"),
618            values: sov,
619        };
620        let result = BasicConstraints::try_from(attribute);
621        assert!(result.is_err());
622    }
623}