From 1e227d09a893c5fb73f65c82db1257b10ee6aff3 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sat, 13 Apr 2024 20:46:27 +0200 Subject: [PATCH 01/23] Start re-doing KeyUsage --- src/certs/capabilities/key_usage.rs | 268 +++++----------------------- src/certs/capabilities/mod.rs | 25 +-- src/constraints.rs | 12 +- 3 files changed, 55 insertions(+), 250 deletions(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 64387a1..be1602f 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -4,7 +4,7 @@ use std::str::FromStr; -use der::asn1::{OctetString, SetOfVec}; +use der::asn1::{BitString, OctetString, SetOfVec}; use der::{Any, Encode, Tag, Tagged}; use spki::ObjectIdentifier; use x509_cert::attr::Attribute; @@ -19,247 +19,63 @@ use super::*; /// restriction might be employed when a key that could be used for more than one operation is to /// be restricted. See pub enum KeyUsage { - /// This purpose is set to true when the subject public key is used for verifying digital + /// This purpose is set when the subject public key is used for verifying digital /// signatures, other than signatures on certificates (`key_cert_sign`) and CRLs (`crl_sign`). - DigitalSignature(bool), - /// This purpose is set to true when the subject public key is used for verifying signatures on - /// certificate revocation lists. - CrlSign(bool), - /// This purpose is set to true when the subject public key is used for verifying digital + DigitalSignature = 1, + /// This purpose is set when the subject public key is used for verifying digital /// signatures, other than signatures on certificates (`key_cert_sign`) and CRLs (`crl_sign`). /// It is used to provide a non-repudiation service that protects against the signing entity /// falsely denying some action. In the case of later conflict, a reliable third party may /// determine the authenticity of the signed data. This was called `non_repudiation` in older /// revisions of the X.509 specification. - ContentCommitment(bool), - /// This purpose is set to true when the subject public key is used for enciphering private or + ContentCommitment = 2, + /// This purpose is set when the subject public key is used for enciphering private or /// secret keys. - KeyEncipherment(bool), - /// This purpose is set to true when the subject public key is used for directly enciphering raw + KeyEncipherment = 4, + /// This purpose is set when the subject public key is used for directly enciphering raw /// user data without the use of an intermediate symmetric cipher. - DataEncipherment(bool), - /// This purpose is set to true when the subject public key is used for key agreement. For + DataEncipherment = 8, + /// This purpose is set when the subject public key is used for key agreement. For /// example, when a Diffie-Hellman key is to be used for key management, then this purpose is - /// set to true. - KeyAgreement(bool), - /// This purpose is set to true when the subject public key is used for verifying signatures on + /// set. + KeyAgreement = 16, + /// This purpose is set when the subject public key is used for verifying signatures on /// public key certificates. If this purpose is set to true then ca must be true in the /// `BasicConstraints` extension. - KeyCertSign(bool), - /// When this purposes is set to true and the `key_agreement` purpose is also set, the subject + KeyCertSign = 32, + /// This purpose is set when the subject public key is used for verifying signatures on + /// certificate revocation lists. + CrlSign = 64, + /// When this purpose is set and the `key_agreement` purpose is also set, the subject /// public key may be used only for enciphering data while performing key agreement. The - /// `KeyAgreement` capability must be set to `true` for this. - EncipherOnly(bool), - /// When this purposes is set to true and the `key_agreement` purpose is also set, the subject + /// `KeyAgreement` capability must be set for this. + EncipherOnly = 128, + /// When this purpose is set and the `key_agreement` purpose is also set, the subject /// public key may be used only for deciphering data while performing key agreement. The - /// `KeyAgreement` capability must be set to `true` for this. - DecipherOnly(bool), -} - -impl From for bool { - fn from(value: KeyUsage) -> Self { - match value { - KeyUsage::DigitalSignature(val) => val, - KeyUsage::CrlSign(val) => val, - KeyUsage::ContentCommitment(val) => val, - KeyUsage::KeyEncipherment(val) => val, - KeyUsage::DataEncipherment(val) => val, - KeyUsage::KeyAgreement(val) => val, - KeyUsage::KeyCertSign(val) => val, - KeyUsage::EncipherOnly(val) => val, - KeyUsage::DecipherOnly(val) => val, - } - } -} - -impl TryFrom for KeyUsage { - type Error = InvalidInput; - - /// Performs the conversion. - /// - /// Fails, if the input attribute does not contain exactly one value, or if the input attribute - /// does not contain a boolean value. Also fails if the OID of the attribute does not match any - /// known KeyUsage variant. - fn try_from(value: Attribute) -> Result { - // PRETTYFYME: I know this is a bit of a mess, but it works. If anyone wants to make it - // prettier, feel free to do so. - - // Check if the attribute contains exactly one value - if value.values.len() != 1usize { - return Err(InvalidInput::IncompatibleVariantForConversion { reason: "This attribute does not store exactly one value, as would be expected for a KeyUsage attribute".to_string() }); - } - let sov = value.values.get(0); - - // The first value inside the Attribute is a SetOfVec. We need to look inside the SetOfVec to - // find the actual attribute we are interested in. - if let Some(inner_value) = sov { - if inner_value.tag() != Tag::Boolean { - return Err(InvalidInput::IncompatibleVariantForConversion { reason: format!("Only Any objects with boolean tags can be converted to a KeyUsage enum variant. Expected Tag::Boolean, found {:?}", inner_value.tag()) }); - } - // This is how booleans are apparently encoded in ASN.1 - let boolean_value = match inner_value.value() { - &[0x00] => false, - &[0xFF] | &[0x01] => true, - _ => { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: "Encountered unexpected value for Boolean tag".to_string(), - }); - } - }; - // Now we have to match the OID of the attribute to the known KeyUsage variants - return Ok(match value.oid.to_string().as_str() { - super::OID_KEY_USAGE_CONTENT_COMMITMENT => KeyUsage::ContentCommitment(boolean_value), - super::OID_KEY_USAGE_CRL_SIGN => KeyUsage::CrlSign(boolean_value), - super::OID_KEY_USAGE_DATA_ENCIPHERMENT => KeyUsage::DataEncipherment(boolean_value), - super::OID_KEY_USAGE_DECIPHER_ONLY => KeyUsage::DecipherOnly(boolean_value), - super::OID_KEY_USAGE_DIGITAL_SIGNATURE => KeyUsage::DigitalSignature(boolean_value), - super::OID_KEY_USAGE_ENCIPHER_ONLY => KeyUsage::EncipherOnly(boolean_value), - super::OID_KEY_USAGE_KEY_AGREEMENT => KeyUsage::KeyAgreement(boolean_value), - #[allow(unreachable_patterns)] // cargo thinks the below pattern is unreachable. - super::OID_KEY_USAGE_KEY_CERT_SIGN => KeyUsage::KeyCertSign(boolean_value), - super::OID_KEY_USAGE_KEY_ENCIPHERMENT => KeyUsage::KeyEncipherment(boolean_value), - // If the OID does not match any known KeyUsage variant, we return an error - _ => { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: format!("The OID of the attribute does not match any known KeyUsage variant. Found OID \"{}\"", value.oid) - }, - ) - } - }); - } - // If the attribute does not contain a value, we return an error - Err(InvalidInput::IncompatibleVariantForConversion { - reason: "The attribute does not contain a value".to_string(), - }) - } -} - -impl TryFrom for KeyUsage { - type Error = InvalidInput; - - /// Performs the conversion. - /// - /// Fails, if the input attribute does not contain a boolean value. Also fails if the OID of - /// the attribute does not match any known KeyUsage variant, especially when the unknown OID is - /// marked as "critical" - fn try_from(value: Extension) -> Result { - #[allow(unreachable_patterns)] - if value.critical - && !matches!( - value.extn_id.to_string().as_str(), - OID_KEY_USAGE_CONTENT_COMMITMENT - | OID_KEY_USAGE_CRL_SIGN - | OID_KEY_USAGE_DATA_ENCIPHERMENT - | OID_KEY_USAGE_DATA_ENCIPHERMENT - | OID_KEY_USAGE_DECIPHER_ONLY - | OID_KEY_USAGE_DIGITAL_SIGNATURE - | OID_KEY_USAGE_ENCIPHER_ONLY - | OID_KEY_USAGE_KEY_AGREEMENT - | OID_KEY_USAGE_KEY_CERT_SIGN - | OID_KEY_USAGE_KEY_ENCIPHERMENT - ) - { - // Error if we encounter a "critical" X.509 extension which we do not know of - return Err(InvalidInput::UnknownCriticalExtension { oid: value.extn_id }); - } - - let boolean_value = match value.extn_value.as_bytes() { - &[0x00] => false, - &[0xFF] | &[0x01] => true, - _ => { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: "Encountered unexpected value for Boolean tag".to_string(), - }); - } - }; - // Now we have to match the OID of the attribute to the known KeyUsage variants - #[allow(unreachable_patterns)] - return Ok(match value.extn_id.to_string().as_str() { - super::OID_KEY_USAGE_CONTENT_COMMITMENT => KeyUsage::ContentCommitment(boolean_value), - super::OID_KEY_USAGE_CRL_SIGN => KeyUsage::CrlSign(boolean_value), - super::OID_KEY_USAGE_DATA_ENCIPHERMENT => KeyUsage::DataEncipherment(boolean_value), - super::OID_KEY_USAGE_DECIPHER_ONLY => KeyUsage::DecipherOnly(boolean_value), - super::OID_KEY_USAGE_DIGITAL_SIGNATURE => KeyUsage::DigitalSignature(boolean_value), - super::OID_KEY_USAGE_ENCIPHER_ONLY => KeyUsage::EncipherOnly(boolean_value), - super::OID_KEY_USAGE_KEY_AGREEMENT => KeyUsage::KeyAgreement(boolean_value), - super::OID_KEY_USAGE_KEY_CERT_SIGN => KeyUsage::KeyCertSign(boolean_value), - super::OID_KEY_USAGE_KEY_ENCIPHERMENT => KeyUsage::KeyEncipherment(boolean_value), - // If the OID does not match any known KeyUsage variant, we return an error - _ => { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: format!("The OID of the attribute does not match any known KeyUsage variant. Found OID \"{}\"", value.extn_id) - }, - ) - } - }); - } -} - -impl From for Any { - fn from(value: KeyUsage) -> Self { - Any::new(der::Tag::Boolean,match bool::from(value) { - true => vec![0xff], - false => vec![0x00], - }, - ).expect("Error occurred when converting BasicConstraints bool to der::Any. Please report this crash at https://github.com/polyphony-chat/polyproto.") - } + /// `KeyAgreement` capability must be set for this. + DecipherOnly = 256, } -impl From for ObjectIdentifier { - fn from(value: KeyUsage) -> Self { - let result = match value { - KeyUsage::DigitalSignature(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_DIGITAL_SIGNATURE) - } - KeyUsage::CrlSign(_) => ObjectIdentifier::from_str(super::OID_KEY_USAGE_CRL_SIGN), - KeyUsage::ContentCommitment(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_CONTENT_COMMITMENT) - } - KeyUsage::KeyEncipherment(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_KEY_ENCIPHERMENT) - } - KeyUsage::DataEncipherment(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_DATA_ENCIPHERMENT) - } - KeyUsage::KeyAgreement(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_KEY_AGREEMENT) - } - KeyUsage::KeyCertSign(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_KEY_CERT_SIGN) - } - KeyUsage::EncipherOnly(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_ENCIPHER_ONLY) - } - KeyUsage::DecipherOnly(_) => { - ObjectIdentifier::from_str(super::OID_KEY_USAGE_DECIPHER_ONLY) - } - }; - result.expect("Error occurred when converting KeyUsage enum to ObjectIdentifier. Please report this crash at https://github.com/polyphony-chat/polyproto.") - } +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct KeyUsages { + pub key_usages: Vec, } -impl From for Attribute { - fn from(value: KeyUsage) -> Self { - // Creating a Any from a bool is really simple, so we can expect this to never fail. - let any_val = Any::from(value); - let mut sov = SetOfVec::new(); - // .insert() only fails if the value is not unique. We are inserting a single value, so this - // should never fail. See tests below for verification. - sov.insert(any_val).expect("Error occurred when inserting KeyUsage into der::Any to SetOfVec. Please report this crash at https://github.com/polyphony-chat/polyproto"); - Attribute { - oid: value.into(), - values: sov, +impl KeyUsages { + pub fn new(key_usages: &[KeyUsage]) -> Self { + KeyUsages { + key_usages: key_usages.to_vec(), } } } -impl From for Extension { - fn from(value: KeyUsage) -> Self { - Extension { - extn_id: value.into(), - critical: true, - extn_value: OctetString::new(bool::from(value).to_der().expect("Error occured when trying to convert bool to DER. Please report this crash at https://github.com/polyphony-chat/polyproto")).expect("Error occured when trying to convert bool to an OctetString. Please report this crash at https://github.com/polyphony-chat/polyproto"), +impl From for BitString { + fn from(value: KeyUsages) -> Self { + let mut number: usize = 0; + for item in value.key_usages.iter() { + number += *item as usize; } + BitString::from_bytes(number.to_be_bytes().as_slice()).expect("Error when trying to convert KeyUsages to BitString. Please report this error to https://github.com/polyphony-chat/polyproto") } } @@ -269,9 +85,13 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] - fn key_usage_to_extension() { - let key_usage = KeyUsage::KeyCertSign(true); - let extension = Extension::from(key_usage); - dbg!(extension); + fn key_usages_to_bitstring() { + let key_usages = KeyUsages::new(&[ + KeyUsage::CrlSign, + KeyUsage::EncipherOnly, + KeyUsage::KeyAgreement, + ]); + let bitstring = BitString::from(key_usages); + dbg!(bitstring) } } diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index 64beadb..5d8fce3 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -38,6 +38,8 @@ pub const OID_KEY_USAGE_ENCIPHER_ONLY: &str = "1.3.6.1.5.5.7.3.7"; pub const OID_KEY_USAGE_DECIPHER_ONLY: &str = "1.3.6.1.5.5.7.3.6"; /// Object Identifier for the BasicConstraints variant. pub const OID_BASIC_CONSTRAINTS: &str = "2.5.29.19"; +/// Object Identifier for the KeyUsage flag. +pub const OID_KEY_USAGE: &str = "2.5.29.15"; #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] /// Capabilities which an ID-Cert or ID-CSR might have. For ID-Certs, you'd find these capabilities @@ -70,7 +72,7 @@ impl Capabilities { /// Sane default for actor [IdCsr]/[IdCert] [Capabilities]. Uses the DigitalSignature flag, /// not the ContentCommitment flag. pub fn default_actor() -> Self { - let key_usage = vec![KeyUsage::DigitalSignature(true)]; + let key_usage = vec![KeyUsage::DigitalSignature]; let basic_constraints = BasicConstraints { ca: false, path_length: None, @@ -83,7 +85,7 @@ impl Capabilities { /// Sane default for home server [IdCsr]/[IdCert] [Capabilities]. pub fn default_home_server() -> Self { - let key_usage = vec![KeyUsage::KeyCertSign(true)]; + let key_usage = vec![KeyUsage::KeyCertSign]; let basic_constraints = BasicConstraints { ca: true, path_length: Some(1), @@ -151,7 +153,7 @@ impl TryFrom for Attributes { let insertion = sov.insert(Attribute::from(*item)); if insertion.is_err() { return Err(ConstraintError::Malformed(Some("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); - } + }*/ } let insertion = sov.insert(Attribute::from(value.basic_constraints)); if insertion.is_err() { @@ -401,23 +403,6 @@ mod test_basic_constraints_from_attribute { assert!(result.is_ok()); } - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg_attr(not(target_arch = "wasm32"), test)] - fn test_basic_constraints_wrong_value_amount() { - let bc = BasicConstraints { - ca: true, - path_length: Some(0), - }; - let mut attribute = Attribute::from(bc); - attribute - .values - .insert(Any::from(KeyUsage::DataEncipherment(false))) - .unwrap(); - let result = BasicConstraints::try_from(attribute); - dbg!(&result); - assert!(result.is_err()); - } - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_basic_constraints_wrong_value_type() { diff --git a/src/constraints.rs b/src/constraints.rs index f5705a9..62f2567 100644 --- a/src/constraints.rs +++ b/src/constraints.rs @@ -144,22 +144,22 @@ impl Constrained for Capabilities { // Iterate over all the entries in the KeyUsage vector, check if they exist/are true for item in self.key_usage.iter() { - if !has_only_encipher && item == &KeyUsage::EncipherOnly(true) { + if !has_only_encipher && item == &KeyUsage::EncipherOnly { has_only_encipher = true; } - if !has_only_decipher && item == &KeyUsage::DecipherOnly(true) { + if !has_only_decipher && item == &KeyUsage::DecipherOnly { has_only_decipher = true; } - if !has_key_agreement && item == &KeyUsage::KeyAgreement(true) { + if !has_key_agreement && item == &KeyUsage::KeyAgreement { has_key_agreement = true; } - if !has_key_agreement && item == &KeyUsage::ContentCommitment(true) { + if !has_key_agreement && item == &KeyUsage::ContentCommitment { can_commit_content = true; } - if !has_key_agreement && item == &KeyUsage::DigitalSignature(true) { + if !has_key_agreement && item == &KeyUsage::DigitalSignature { can_sign = true; } - if !has_key_agreement && item == &KeyUsage::KeyCertSign(true) { + if !has_key_agreement && item == &KeyUsage::KeyCertSign { key_cert_sign = true; } } From 6b6397b2ad5c0922af62a448fd2b5fe9861b896e Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sat, 13 Apr 2024 20:48:09 +0200 Subject: [PATCH 02/23] Change KeyUsage to KeyUsages --- src/certs/capabilities/mod.rs | 62 +++++++++++++++++------------------ src/constraints.rs | 2 +- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index 5d8fce3..e0b2f11 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -50,7 +50,7 @@ pub const OID_KEY_USAGE: &str = "2.5.29.15"; /// are relevant to polyproto certificates. pub struct Capabilities { /// The key usage extension defines the purpose of the key contained in the certificate. - pub key_usage: Vec, + pub key_usage: Vec, /// Extension type that defines whether a given certificate is allowed /// to sign additional certificates and what path length restrictions may exist. pub basic_constraints: BasicConstraints, @@ -107,7 +107,7 @@ impl TryFrom for Capabilities { /// to ensure that the constraints are valid according to the X.509 standard and the polyproto /// specification. fn try_from(value: Attributes) -> Result { - let mut key_usages: Vec = Vec::new(); + let mut key_usages: Vec = Vec::new(); let mut basic_constraints = BasicConstraints::default(); let mut num_basic_constraints = 0u8; for item in value.iter() { @@ -122,7 +122,7 @@ impl TryFrom for Capabilities { | OID_KEY_USAGE_KEY_AGREEMENT | OID_KEY_USAGE_KEY_CERT_SIGN | OID_KEY_USAGE_KEY_ENCIPHERMENT => { - key_usages.push(KeyUsage::try_from(item.clone())?); + key_usages.push(KeyUsages::try_from(item.clone())?); } OID_BASIC_CONSTRAINTS => { num_basic_constraints += 1; @@ -189,7 +189,7 @@ impl TryFrom for Capabilities { /// these resulting [Capabilities]. fn try_from(value: Extensions) -> Result { let mut basic_constraints: BasicConstraints = BasicConstraints::default(); - let mut key_usage: Vec = Vec::new(); + let mut key_usage: Vec = Vec::new(); for item in value.iter() { #[allow(unreachable_patterns)] // cargo thinks that we have an unreachable pattern here match item.extn_id.to_string().as_str() { @@ -205,7 +205,7 @@ impl TryFrom for Capabilities { | OID_KEY_USAGE_KEY_AGREEMENT | OID_KEY_USAGE_KEY_CERT_SIGN | OID_KEY_USAGE_KEY_ENCIPHERMENT => { - key_usage.push(KeyUsage::try_from(item.clone())?) + key_usage.push(KeyUsages::try_from(item.clone())?) }, _ => return Err(InvalidInput::ConstraintError(ConstraintError::Malformed(Some(format!("Invalid OID found for converting this set of Extensions to Capabilities: {} is not a valid OID for BasicConstraints or KeyUsages", item.extn_id))))) }; @@ -225,27 +225,27 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_key_usage_to_object_identifier() { - let _ = ObjectIdentifier::from(KeyUsage::DigitalSignature(true)); - let _ = ObjectIdentifier::from(KeyUsage::CrlSign(true)); - let _ = ObjectIdentifier::from(KeyUsage::ContentCommitment(true)); - let _ = ObjectIdentifier::from(KeyUsage::KeyEncipherment(true)); - let _ = ObjectIdentifier::from(KeyUsage::DataEncipherment(true)); - let _ = ObjectIdentifier::from(KeyUsage::KeyAgreement(true)); - let _ = ObjectIdentifier::from(KeyUsage::KeyCertSign(true)); - let _ = ObjectIdentifier::from(KeyUsage::EncipherOnly(true)); - let _ = ObjectIdentifier::from(KeyUsage::DecipherOnly(true)); + let _ = ObjectIdentifier::from(KeyUsages::DigitalSignature(true)); + let _ = ObjectIdentifier::from(KeyUsages::CrlSign(true)); + let _ = ObjectIdentifier::from(KeyUsages::ContentCommitment(true)); + let _ = ObjectIdentifier::from(KeyUsages::KeyEncipherment(true)); + let _ = ObjectIdentifier::from(KeyUsages::DataEncipherment(true)); + let _ = ObjectIdentifier::from(KeyUsages::KeyAgreement(true)); + let _ = ObjectIdentifier::from(KeyUsages::KeyCertSign(true)); + let _ = ObjectIdentifier::from(KeyUsages::EncipherOnly(true)); + let _ = ObjectIdentifier::from(KeyUsages::DecipherOnly(true)); } fn test_key_usage_to_attribute(val: bool) { - let _ = Attribute::from(KeyUsage::DigitalSignature(val)); - let _ = Attribute::from(KeyUsage::CrlSign(val)); - let _ = Attribute::from(KeyUsage::ContentCommitment(val)); - let _ = Attribute::from(KeyUsage::KeyEncipherment(val)); - let _ = Attribute::from(KeyUsage::DataEncipherment(val)); - let _ = Attribute::from(KeyUsage::KeyAgreement(val)); - let _ = Attribute::from(KeyUsage::KeyCertSign(val)); - let _ = Attribute::from(KeyUsage::EncipherOnly(val)); - let _ = Attribute::from(KeyUsage::DecipherOnly(val)); + let _ = Attribute::from(KeyUsages::DigitalSignature(val)); + let _ = Attribute::from(KeyUsages::CrlSign(val)); + let _ = Attribute::from(KeyUsages::ContentCommitment(val)); + let _ = Attribute::from(KeyUsages::KeyEncipherment(val)); + let _ = Attribute::from(KeyUsages::DataEncipherment(val)); + let _ = Attribute::from(KeyUsages::KeyAgreement(val)); + let _ = Attribute::from(KeyUsages::KeyCertSign(val)); + let _ = Attribute::from(KeyUsages::EncipherOnly(val)); + let _ = Attribute::from(KeyUsages::DecipherOnly(val)); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] @@ -312,9 +312,9 @@ mod test_key_usage_from_attribute { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_key_usage_from_attribute() { - let key_usage = KeyUsage::ContentCommitment(true); + let key_usage = KeyUsages::ContentCommitment(true); let attribute = Attribute::from(key_usage); - let result = KeyUsage::try_from(attribute); + let result = KeyUsages::try_from(attribute); dbg!(&result); assert!(result.is_ok()); } @@ -322,13 +322,13 @@ mod test_key_usage_from_attribute { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_key_usage_wrong_value_amount() { - let key_usage = KeyUsage::ContentCommitment(true); + let key_usage = KeyUsages::ContentCommitment(true); let mut attribute = Attribute::from(key_usage); attribute .values - .insert(Any::from(KeyUsage::DataEncipherment(false))) + .insert(Any::from(KeyUsages::DataEncipherment(false))) .unwrap(); - let result = KeyUsage::try_from(attribute); + let result = KeyUsages::try_from(attribute); dbg!(&result); assert!(result.is_err()); } @@ -343,7 +343,7 @@ mod test_key_usage_from_attribute { oid: ObjectIdentifier::from_str(OID_KEY_USAGE_CONTENT_COMMITMENT).unwrap(), values: sov, }; - let result = KeyUsage::try_from(attribute); + let result = KeyUsages::try_from(attribute); dbg!(&result); assert!(result.is_err()); } @@ -358,7 +358,7 @@ mod test_key_usage_from_attribute { oid: ObjectIdentifier::from_str("1.2.4.2.1.1.1.1.1.1.1.1.1.1.161.69").unwrap(), values: sov, }; - let result = KeyUsage::try_from(attribute); + let result = KeyUsages::try_from(attribute); dbg!(&result); assert!(result.is_err()); } @@ -373,7 +373,7 @@ mod test_key_usage_from_attribute { oid: ObjectIdentifier::from_str(OID_KEY_USAGE_CONTENT_COMMITMENT).unwrap(), values: sov, }; - let result = KeyUsage::try_from(attribute); + let result = KeyUsages::try_from(attribute); dbg!(&result); assert!(result.is_err()); } diff --git a/src/constraints.rs b/src/constraints.rs index 62f2567..869ea71 100644 --- a/src/constraints.rs +++ b/src/constraints.rs @@ -5,7 +5,7 @@ use der::Length; use x509_cert::name::Name; -use crate::certs::capabilities::{Capabilities, KeyUsage}; +use crate::certs::capabilities::{Capabilities, KeyUsage, KeyUsages}; use crate::certs::idcert::IdCert; use crate::certs::idcerttbs::IdCertTbs; use crate::certs::idcsr::IdCsr; From 32ff789173ab5d4cc2deb04b43f5f690052a9024 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sat, 13 Apr 2024 20:59:37 +0200 Subject: [PATCH 03/23] stubs for missing trait implementations --- src/certs/capabilities/key_usage.rs | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index be1602f..1733e16 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -79,6 +79,34 @@ impl From for BitString { } } +impl TryFrom for KeyUsages { + type Error = InvalidInput; + + fn try_from(value: Attribute) -> Result { + todo!() + } +} + +impl TryFrom for KeyUsages { + type Error = InvalidInput; + + fn try_from(value: Extension) -> Result { + todo!() + } +} + +impl From for Attribute { + fn from(value: KeyUsages) -> Self { + todo!() + } +} + +impl From for Extension { + fn from(value: KeyUsages) -> Self { + todo!() + } +} + #[cfg(test)] mod test { use super::*; From 547152006ffc555c7d3f44bc7ee330ff5f68c15b Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sat, 13 Apr 2024 21:16:08 +0200 Subject: [PATCH 04/23] fix typo --- src/certs/capabilities/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index e0b2f11..790da86 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -153,7 +153,7 @@ impl TryFrom for Attributes { let insertion = sov.insert(Attribute::from(*item)); if insertion.is_err() { return Err(ConstraintError::Malformed(Some("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); - }*/ + } } let insertion = sov.insert(Attribute::from(value.basic_constraints)); if insertion.is_err() { From 9cc4932e75e374740b0ce21e8c77b8603e061c61 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sat, 13 Apr 2024 21:16:13 +0200 Subject: [PATCH 05/23] start impl --- src/certs/capabilities/key_usage.rs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 1733e16..47cd580 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -83,7 +83,26 @@ impl TryFrom for KeyUsages { type Error = InvalidInput; fn try_from(value: Attribute) -> Result { - todo!() + if value.tag() != Tag::BitString { + return Err(InvalidInput::IncompatibleVariantForConversion { + reason: format!("Expected BitString, found {}", value.tag()), + }); + } + match value.values.len() { + 0 => return Ok(KeyUsages::new(&[])), + 1 => (), + _ => { + return Err(InvalidInput::ConstraintError( + ConstraintError::OutOfBounds { + lower: 0, + upper: 1, + actual: value.values.len().to_string(), + reason: Some("Too many values to be a valid KeyUsages value".to_string()), + }, + )) + } + }; + let inner_value = value.values.get(0).expect("Illegal state. Please report this error to https://github.com/polyphony-chat/polyproto"); } } From d79a48e8d955c7ea0f6d55a028b0c53effbe7cc9 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 11:56:27 +0200 Subject: [PATCH 06/23] (untested) impl try_from attribute for keyusages --- src/certs/capabilities/key_usage.rs | 76 ++++++++++++++++++++++++++++- src/errors/base.rs | 2 + 2 files changed, 77 insertions(+), 1 deletion(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 47cd580..2aff445 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -10,7 +10,7 @@ use spki::ObjectIdentifier; use x509_cert::attr::Attribute; use x509_cert::ext::Extension; -use crate::errors::base::InvalidInput; +use crate::errors::base::{InvalidInput, UnsuccessfulConversion}; use super::*; @@ -56,6 +56,32 @@ pub enum KeyUsage { DecipherOnly = 256, } +impl TryFrom for KeyUsage { + type Error = UnsuccessfulConversion; + + fn try_from(value: u32) -> Result { + if value > 256 { + return Err(UnsuccessfulConversion::InvalidInput( + "Value too big to match this enums' variants".to_string(), + )); + } + match value { + x if x == KeyUsage::DigitalSignature as u32 => Ok(KeyUsage::DigitalSignature), + x if x == KeyUsage::ContentCommitment as u32 => Ok(KeyUsage::ContentCommitment), + x if x == KeyUsage::KeyEncipherment as u32 => Ok(KeyUsage::KeyEncipherment), + x if x == KeyUsage::DataEncipherment as u32 => Ok(KeyUsage::DataEncipherment), + x if x == KeyUsage::KeyAgreement as u32 => Ok(KeyUsage::KeyAgreement), + x if x == KeyUsage::KeyCertSign as u32 => Ok(KeyUsage::KeyCertSign), + x if x == KeyUsage::CrlSign as u32 => Ok(KeyUsage::CrlSign), + x if x == KeyUsage::EncipherOnly as u32 => Ok(KeyUsage::EncipherOnly), + x if x == KeyUsage::DecipherOnly as u32 => Ok(KeyUsage::DecipherOnly), + _ => Err(UnsuccessfulConversion::InvalidInput( + "Value does not match any of the variants".to_string(), + )), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct KeyUsages { pub key_usages: Vec, @@ -103,6 +129,54 @@ impl TryFrom for KeyUsages { } }; let inner_value = value.values.get(0).expect("Illegal state. Please report this error to https://github.com/polyphony-chat/polyproto"); + /* Below, we are doing some operations on bits. RFC 5280 says: + KeyUsage ::= BIT STRING { + digitalSignature (0), + nonRepudiation (1), -- recent editions of X.509 have + -- renamed this bit to contentCommitment + keyEncipherment (2), + dataEncipherment (3), + keyAgreement (4), + keyCertSign (5), + cRLSign (6), + encipherOnly (7), + decipherOnly (8) } + + It is now our task to check, which bits (0 - 8) are set, and to construct KeyUsage variants + from this information. + Note, that BitStrings represent the bits they store in big endian order, meaning that the + most significant bit (MSB) is the first bit in the BitString. + */ + let bitstring = BitString::from_bytes(inner_value.value())?; + // The "starting number" is 2 to the power of len(bitstring) [<-pseudocode]. + // For a bit of length 8, this would be 2^8=256. + let mut starting_number = 2u32.pow(bitstring.bit_len() as u32); + let mut key_usages = Vec::new(); + // We iterate over all bits, check if the current bit is set and try to convert the + // current value of starting_number to a KeyUsage variant. On every iteration, we divide + // starting_number by two, until it equals 1 and thus cannot be divided any further. + for bit in bitstring.bits() { + let bitval = match bit { + true => 1u8, + false => { + divide_starting_number(&mut starting_number); + continue; + } + }; + key_usages.push(KeyUsage::try_from(starting_number as u32)?); + if starting_number == 1 { + // Stop the loop if starting_number is already 1. + break; + } + divide_starting_number(&mut starting_number); + } + Ok(KeyUsages::new(&key_usages)) + } +} + +fn divide_starting_number(number: &mut u32) { + if !*number == 1 { + *number /= 2; } } diff --git a/src/errors/base.rs b/src/errors/base.rs index 794506e..30d49da 100644 --- a/src/errors/base.rs +++ b/src/errors/base.rs @@ -33,6 +33,8 @@ pub enum InvalidInput { UnknownCriticalExtension { oid: ObjectIdentifier }, #[error(transparent)] ConstraintError(#[from] ConstraintError), + #[error(transparent)] + UnsuccessfulConversion(#[from] UnsuccessfulConversion), } #[derive(Error, Debug, PartialEq, Clone)] From e78e425dd856471528fbd5550a88ecff6d10a160 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 12:44:12 +0200 Subject: [PATCH 07/23] Add default derive to KeyUsages --- src/certs/capabilities/key_usage.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 2aff445..52405d7 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -82,7 +82,7 @@ impl TryFrom for KeyUsage { } } -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] pub struct KeyUsages { pub key_usages: Vec, } From 4dae4e755e2b024bc09a6a095acec63e1f6ab360 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 12:44:21 +0200 Subject: [PATCH 08/23] begin patching up mod.rs a little --- src/certs/capabilities/mod.rs | 21 +++++++-------------- 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index 790da86..c6694ba 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -50,7 +50,7 @@ pub const OID_KEY_USAGE: &str = "2.5.29.15"; /// are relevant to polyproto certificates. pub struct Capabilities { /// The key usage extension defines the purpose of the key contained in the certificate. - pub key_usage: Vec, + pub key_usage: KeyUsages, /// Extension type that defines whether a given certificate is allowed /// to sign additional certificates and what path length restrictions may exist. pub basic_constraints: BasicConstraints, @@ -72,7 +72,7 @@ impl Capabilities { /// Sane default for actor [IdCsr]/[IdCert] [Capabilities]. Uses the DigitalSignature flag, /// not the ContentCommitment flag. pub fn default_actor() -> Self { - let key_usage = vec![KeyUsage::DigitalSignature]; + let key_usage = KeyUsages::new(&[KeyUsage::DigitalSignature]); let basic_constraints = BasicConstraints { ca: false, path_length: None, @@ -85,7 +85,7 @@ impl Capabilities { /// Sane default for home server [IdCsr]/[IdCert] [Capabilities]. pub fn default_home_server() -> Self { - let key_usage = vec![KeyUsage::KeyCertSign]; + let key_usage = KeyUsages::new(&[KeyUsage::KeyCertSign]); let basic_constraints = BasicConstraints { ca: true, path_length: Some(1), @@ -107,22 +107,14 @@ impl TryFrom for Capabilities { /// to ensure that the constraints are valid according to the X.509 standard and the polyproto /// specification. fn try_from(value: Attributes) -> Result { - let mut key_usages: Vec = Vec::new(); + let mut key_usages = KeyUsages::new(&[]); let mut basic_constraints = BasicConstraints::default(); let mut num_basic_constraints = 0u8; for item in value.iter() { match item.oid.to_string().as_str() { #[allow(unreachable_patterns)] // cargo thinks the below pattern is unreachable. - OID_KEY_USAGE_CONTENT_COMMITMENT - | OID_KEY_USAGE_CRL_SIGN - | OID_KEY_USAGE_DATA_ENCIPHERMENT - | OID_KEY_USAGE_DECIPHER_ONLY - | OID_KEY_USAGE_DIGITAL_SIGNATURE - | OID_KEY_USAGE_ENCIPHER_ONLY - | OID_KEY_USAGE_KEY_AGREEMENT - | OID_KEY_USAGE_KEY_CERT_SIGN - | OID_KEY_USAGE_KEY_ENCIPHERMENT => { - key_usages.push(KeyUsages::try_from(item.clone())?); + OID_KEY_USAGE => { + key_usages = KeyUsages::try_from(*item)?; } OID_BASIC_CONSTRAINTS => { num_basic_constraints += 1; @@ -149,6 +141,7 @@ impl TryFrom for Attributes { fn try_from(value: Capabilities) -> Result { value.validate()?; let mut sov = SetOfVec::new(); + // TODO: Impl From for Attributes and then just call that here for item in value.key_usage.iter() { let insertion = sov.insert(Attribute::from(*item)); if insertion.is_err() { From 40a387ca5085703b935ee9e4a56737a016a07eb3 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 16:45:36 +0200 Subject: [PATCH 09/23] port mod.rs to new KeyUsages --- src/certs/capabilities/mod.rs | 35 +++++++++++------------------------ 1 file changed, 11 insertions(+), 24 deletions(-) diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index c6694ba..6954370 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -114,7 +114,7 @@ impl TryFrom for Capabilities { match item.oid.to_string().as_str() { #[allow(unreachable_patterns)] // cargo thinks the below pattern is unreachable. OID_KEY_USAGE => { - key_usages = KeyUsages::try_from(*item)?; + key_usages = KeyUsages::try_from(item.clone())?; } OID_BASIC_CONSTRAINTS => { num_basic_constraints += 1; @@ -141,12 +141,9 @@ impl TryFrom for Attributes { fn try_from(value: Capabilities) -> Result { value.validate()?; let mut sov = SetOfVec::new(); - // TODO: Impl From for Attributes and then just call that here - for item in value.key_usage.iter() { - let insertion = sov.insert(Attribute::from(*item)); - if insertion.is_err() { - return Err(ConstraintError::Malformed(Some("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); - } + let insertion = sov.insert(Attribute::from(value.key_usage)); + if insertion.is_err() { + return Err(ConstraintError::Malformed(Some("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); } let insertion = sov.insert(Attribute::from(value.basic_constraints)); if insertion.is_err() { @@ -163,12 +160,10 @@ impl From for Extensions { /// /// try_from does **not** check whether the resulting [Extensions] are well-formed. fn from(value: Capabilities) -> Self { - let mut vec = Vec::new(); - vec.push(Extension::from(value.basic_constraints)); - for item in value.key_usage.iter() { - vec.push(Extension::from(*item)); - } - vec + vec![ + Extension::from(value.basic_constraints), + Extension::from(value.key_usage), + ] } } @@ -182,23 +177,15 @@ impl TryFrom for Capabilities { /// these resulting [Capabilities]. fn try_from(value: Extensions) -> Result { let mut basic_constraints: BasicConstraints = BasicConstraints::default(); - let mut key_usage: Vec = Vec::new(); + let mut key_usage: KeyUsages = KeyUsages::default(); for item in value.iter() { #[allow(unreachable_patterns)] // cargo thinks that we have an unreachable pattern here match item.extn_id.to_string().as_str() { OID_BASIC_CONSTRAINTS => { basic_constraints = BasicConstraints::try_from(item.clone())? } - OID_KEY_USAGE_CONTENT_COMMITMENT - | OID_KEY_USAGE_CRL_SIGN - | OID_KEY_USAGE_DATA_ENCIPHERMENT - | OID_KEY_USAGE_DECIPHER_ONLY - | OID_KEY_USAGE_DIGITAL_SIGNATURE - | OID_KEY_USAGE_ENCIPHER_ONLY - | OID_KEY_USAGE_KEY_AGREEMENT - | OID_KEY_USAGE_KEY_CERT_SIGN - | OID_KEY_USAGE_KEY_ENCIPHERMENT => { - key_usage.push(KeyUsages::try_from(item.clone())?) + OID_KEY_USAGE => { + key_usage = KeyUsages::try_from(item.clone())? }, _ => return Err(InvalidInput::ConstraintError(ConstraintError::Malformed(Some(format!("Invalid OID found for converting this set of Extensions to Capabilities: {} is not a valid OID for BasicConstraints or KeyUsages", item.extn_id))))) }; From 21347e9e00f4b6a0da8feb3f12bb992d1dfbae2d Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 16:45:46 +0200 Subject: [PATCH 10/23] simplify --- src/certs/capabilities/key_usage.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 52405d7..7671e02 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -156,14 +156,14 @@ impl TryFrom for KeyUsages { // current value of starting_number to a KeyUsage variant. On every iteration, we divide // starting_number by two, until it equals 1 and thus cannot be divided any further. for bit in bitstring.bits() { - let bitval = match bit { + match bit { true => 1u8, false => { divide_starting_number(&mut starting_number); continue; } }; - key_usages.push(KeyUsage::try_from(starting_number as u32)?); + key_usages.push(KeyUsage::try_from(starting_number)?); if starting_number == 1 { // Stop the loop if starting_number is already 1. break; From 97a043a945e12ade21fb4f8458f5c78b48eee4d9 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 16:45:58 +0200 Subject: [PATCH 11/23] port constraints.rs to new KeyUsages --- src/constraints.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/constraints.rs b/src/constraints.rs index 869ea71..7661655 100644 --- a/src/constraints.rs +++ b/src/constraints.rs @@ -143,7 +143,7 @@ impl Constrained for Capabilities { let mut has_key_agreement = false; // Iterate over all the entries in the KeyUsage vector, check if they exist/are true - for item in self.key_usage.iter() { + for item in self.key_usage.key_usages.iter() { if !has_only_encipher && item == &KeyUsage::EncipherOnly { has_only_encipher = true; } From 2038fff9c4e578ecfbe861e0561594cb9e09f16a Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 20:44:50 +0200 Subject: [PATCH 12/23] reexport spki and der --- src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/lib.rs b/src/lib.rs index 005df36..d873b61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,6 +65,9 @@ pub mod errors; pub(crate) mod constraints; +pub use der; +pub use spki; + /// Traits implementing [Constrained] can be validated to be well-formed. This does not guarantee /// that a validated type will always be *correct* in the context it is in. /// From 74f3afd9768cd55a16173037074379a22bd60628 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 20:48:43 +0200 Subject: [PATCH 13/23] Add to_le_bits, to_be_bits, to_bitstring, impl tryfrom extension for keyusages, impl from keyusages for attribute, impl from keyusages for extension --- src/certs/capabilities/key_usage.rs | 194 ++++++++++++++++++++++------ 1 file changed, 153 insertions(+), 41 deletions(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 7671e02..7b7072a 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -5,7 +5,7 @@ use std::str::FromStr; use der::asn1::{BitString, OctetString, SetOfVec}; -use der::{Any, Encode, Tag, Tagged}; +use der::{Any, Decode, Encode, Tag, Tagged}; use spki::ObjectIdentifier; use x509_cert::attr::Attribute; use x509_cert::ext::Extension; @@ -83,52 +83,38 @@ impl TryFrom for KeyUsage { } #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Default)] +/// The KeyUsages struct is a collection of KeyUsage variants. pub struct KeyUsages { + /// Vector of KeyUsage variants. pub key_usages: Vec, } impl KeyUsages { + /// Creates a new KeyUsages struct from a slice of KeyUsage variants. pub fn new(key_usages: &[KeyUsage]) -> Self { KeyUsages { key_usages: key_usages.to_vec(), } } -} -impl From for BitString { - fn from(value: KeyUsages) -> Self { - let mut number: usize = 0; - for item in value.key_usages.iter() { - number += *item as usize; - } - BitString::from_bytes(number.to_be_bytes().as_slice()).expect("Error when trying to convert KeyUsages to BitString. Please report this error to https://github.com/polyphony-chat/polyproto") - } -} - -impl TryFrom for KeyUsages { - type Error = InvalidInput; - - fn try_from(value: Attribute) -> Result { - if value.tag() != Tag::BitString { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: format!("Expected BitString, found {}", value.tag()), - }); - } - match value.values.len() { - 0 => return Ok(KeyUsages::new(&[])), - 1 => (), - _ => { - return Err(InvalidInput::ConstraintError( - ConstraintError::OutOfBounds { - lower: 0, - upper: 1, - actual: value.values.len().to_string(), - reason: Some("Too many values to be a valid KeyUsages value".to_string()), - }, - )) - } - }; - let inner_value = value.values.get(0).expect("Illegal state. Please report this error to https://github.com/polyphony-chat/polyproto"); + /// Converts a byte slice to a KeyUsages struct, given that the byte slice is a bitstring + /// representing the KeyUsages. + /// + /// RFC 5280 says: + /// ```text + /// KeyUsage ::= BIT STRING { + /// digitalSignature (0), + /// nonRepudiation (1), -- recent editions of X.509 have + /// -- renamed this bit to contentCommitment + /// keyEncipherment (2), + /// dataEncipherment (3), + /// keyAgreement (4), + /// keyCertSign (5), + /// cRLSign (6), + /// encipherOnly (7), + /// decipherOnly (8) } + /// ``` + pub fn from_bytes(bytes: &[u8]) -> Result { /* Below, we are doing some operations on bits. RFC 5280 says: KeyUsage ::= BIT STRING { digitalSignature (0), @@ -147,7 +133,7 @@ impl TryFrom for KeyUsages { Note, that BitStrings represent the bits they store in big endian order, meaning that the most significant bit (MSB) is the first bit in the BitString. */ - let bitstring = BitString::from_bytes(inner_value.value())?; + let bitstring = BitString::from_bytes(bytes)?; // The "starting number" is 2 to the power of len(bitstring) [<-pseudocode]. // For a bit of length 8, this would be 2^8=256. let mut starting_number = 2u32.pow(bitstring.bit_len() as u32); @@ -172,6 +158,87 @@ impl TryFrom for KeyUsages { } Ok(KeyUsages::new(&key_usages)) } + + /// Converts the KeyUsages to a bitstring in little endian order. + pub fn to_le_bits(mut self) -> [bool; 9] { + self.key_usages.sort(); + self.key_usages.dedup(); + let mut bit_vec = [false; 9]; + for item in self.key_usages.into_iter() { + match item { + KeyUsage::DigitalSignature => bit_vec[0] = true, + KeyUsage::ContentCommitment => bit_vec[1] = true, + KeyUsage::KeyEncipherment => bit_vec[2] = true, + KeyUsage::DataEncipherment => bit_vec[3] = true, + KeyUsage::KeyAgreement => bit_vec[4] = true, + KeyUsage::KeyCertSign => bit_vec[5] = true, + KeyUsage::CrlSign => bit_vec[6] = true, + KeyUsage::EncipherOnly => bit_vec[7] = true, + KeyUsage::DecipherOnly => bit_vec[8] = true, + } + } + bit_vec + } + + /// Converts the KeyUsages to a bitstring in big endian order. + pub fn to_be_bits(self) -> [bool; 9] { + let mut bit_vec = self.to_le_bits(); + bit_vec.reverse(); + bit_vec + } + + /// Converts the KeyUsages to a [BitString]. + pub fn to_bitstring(self) -> BitString { + let bits = self.to_be_bits(); + let mut bytes = bits + .iter() + .map(|x| if *x { 1 } else { 0 }) + .collect::>(); + while bytes[0] == 0 { + bytes.remove(0); + } + let mut unused_bits: u8 = 0; + while bytes.len() % 8 != 0 { + bytes.push(0); + unused_bits += 1; + } + BitString::new(unused_bits, bytes) + .expect("Error when converting KeyUsages to BitString. Please report this error") + } +} + +impl From for BitString { + fn from(value: KeyUsages) -> Self { + value.to_bitstring() + } +} + +impl TryFrom for KeyUsages { + type Error = InvalidInput; + + fn try_from(value: Attribute) -> Result { + if value.tag() != Tag::BitString { + return Err(InvalidInput::IncompatibleVariantForConversion { + reason: format!("Expected BitString, found {}", value.tag()), + }); + } + match value.values.len() { + 0 => return Ok(KeyUsages::new(&[])), + 1 => (), + _ => { + return Err(InvalidInput::ConstraintError( + ConstraintError::OutOfBounds { + lower: 0, + upper: 1, + actual: value.values.len().to_string(), + reason: Some("Too many values to be a valid KeyUsages value".to_string()), + }, + )) + } + }; + let inner_value = value.values.get(0).expect("Illegal state. Please report this error to https://github.com/polyphony-chat/polyproto"); + KeyUsages::from_bytes(inner_value.value()) + } } fn divide_starting_number(number: &mut u32) { @@ -184,19 +251,47 @@ impl TryFrom for KeyUsages { type Error = InvalidInput; fn try_from(value: Extension) -> Result { - todo!() + if value.extn_id.to_string().as_str() != OID_KEY_USAGE { + return Err(InvalidInput::IncompatibleVariantForConversion { + reason: format!( + "Expected OID {} for KeyUsages, found OID {}", + OID_KEY_USAGE, value.extn_id + ), + }); + } + // here we need to NOT just do .as_bytes() but to somehow get the actual value in this + // octetstring + // TODO + KeyUsages::from_bytes(value.extn_value.as_bytes()) } } impl From for Attribute { fn from(value: KeyUsages) -> Self { - todo!() + let mut sov = SetOfVec::new(); + let bitstring = value.to_bitstring(); + sov.insert( + Any::from_der(&bitstring.to_der().expect( + "Error occurred when converting blah blah blah this will be tryfrom anyways soon", + )) + .expect("this is tryfrom soon lalalalalla"), + ) + .expect("wow"); + dbg!(&sov); + Attribute { + oid: ObjectIdentifier::from_str(OID_KEY_USAGE).expect( + "Error occurred when converting KeyUsages to ObjectIdentifier. Please report this crash", + ), + values: sov, + } } } impl From for Extension { fn from(value: KeyUsages) -> Self { - todo!() + let bitstring = value.to_bitstring(); + let any = Any::from_der(&bitstring.to_der().expect("guh")).expect("Error occurred when converting KeyUsages to BitString. Please report this crash at https://github.com/polyphony-chat/polyproto"); + Extension { extn_id: ObjectIdentifier::from_str(OID_KEY_USAGE).expect("Error occurred when converting KeyUsages to ObjectIdentifier. Please report this crash at https://github.com/polyphony-chat/polyproto"), critical: true, extn_value: OctetString::new(any.to_der().expect("Error occurred when converting KeyUsages to DER. Please report this crash at https://github.com/polyphony-chat/polyproto")).expect("Error occurred when creating OctetString. Please report this crash at https://github.com/polyphony-chat/polyproto") } } } @@ -213,6 +308,23 @@ mod test { KeyUsage::KeyAgreement, ]); let bitstring = BitString::from(key_usages); - dbg!(bitstring) + dbg!(bitstring); + } + + #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] + #[cfg_attr(not(target_arch = "wasm32"), test)] + fn key_usages_vec_sorts_correctly() { + #[allow(clippy::useless_vec)] + let mut vec = vec![ + KeyUsage::ContentCommitment, + KeyUsage::EncipherOnly, + KeyUsage::DataEncipherment, + KeyUsage::DigitalSignature, + ]; + vec.sort(); + assert!(*vec.get(0).unwrap() == KeyUsage::DigitalSignature); + assert!(*vec.get(1).unwrap() == KeyUsage::ContentCommitment); + assert!(*vec.get(2).unwrap() == KeyUsage::DataEncipherment); + assert!(*vec.get(3).unwrap() == KeyUsage::EncipherOnly); } } From bf859f754688471ac07ec6e6e2f18b5eaba72f42 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 20:48:58 +0200 Subject: [PATCH 14/23] remove outdated tests, mark as todo --- src/certs/capabilities/mod.rs | 44 ++++++----------------------------- 1 file changed, 7 insertions(+), 37 deletions(-) diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index 6954370..c70a3b6 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -185,6 +185,7 @@ impl TryFrom for Capabilities { basic_constraints = BasicConstraints::try_from(item.clone())? } OID_KEY_USAGE => { + dbg!("KeyUsage to Capabilities"); key_usage = KeyUsages::try_from(item.clone())? }, _ => return Err(InvalidInput::ConstraintError(ConstraintError::Malformed(Some(format!("Invalid OID found for converting this set of Extensions to Capabilities: {} is not a valid OID for BasicConstraints or KeyUsages", item.extn_id))))) @@ -205,27 +206,13 @@ mod test { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_key_usage_to_object_identifier() { - let _ = ObjectIdentifier::from(KeyUsages::DigitalSignature(true)); - let _ = ObjectIdentifier::from(KeyUsages::CrlSign(true)); - let _ = ObjectIdentifier::from(KeyUsages::ContentCommitment(true)); - let _ = ObjectIdentifier::from(KeyUsages::KeyEncipherment(true)); - let _ = ObjectIdentifier::from(KeyUsages::DataEncipherment(true)); - let _ = ObjectIdentifier::from(KeyUsages::KeyAgreement(true)); - let _ = ObjectIdentifier::from(KeyUsages::KeyCertSign(true)); - let _ = ObjectIdentifier::from(KeyUsages::EncipherOnly(true)); - let _ = ObjectIdentifier::from(KeyUsages::DecipherOnly(true)); + // TODO + todo!() } fn test_key_usage_to_attribute(val: bool) { - let _ = Attribute::from(KeyUsages::DigitalSignature(val)); - let _ = Attribute::from(KeyUsages::CrlSign(val)); - let _ = Attribute::from(KeyUsages::ContentCommitment(val)); - let _ = Attribute::from(KeyUsages::KeyEncipherment(val)); - let _ = Attribute::from(KeyUsages::DataEncipherment(val)); - let _ = Attribute::from(KeyUsages::KeyAgreement(val)); - let _ = Attribute::from(KeyUsages::KeyCertSign(val)); - let _ = Attribute::from(KeyUsages::EncipherOnly(val)); - let _ = Attribute::from(KeyUsages::DecipherOnly(val)); + // TODO + todo!() } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] @@ -292,25 +279,8 @@ mod test_key_usage_from_attribute { #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_key_usage_from_attribute() { - let key_usage = KeyUsages::ContentCommitment(true); - let attribute = Attribute::from(key_usage); - let result = KeyUsages::try_from(attribute); - dbg!(&result); - assert!(result.is_ok()); - } - - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg_attr(not(target_arch = "wasm32"), test)] - fn test_key_usage_wrong_value_amount() { - let key_usage = KeyUsages::ContentCommitment(true); - let mut attribute = Attribute::from(key_usage); - attribute - .values - .insert(Any::from(KeyUsages::DataEncipherment(false))) - .unwrap(); - let result = KeyUsages::try_from(attribute); - dbg!(&result); - assert!(result.is_err()); + // TODO + todo!() } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] From 1b1391a66b276bc453bbcb077c179997a2a6c04e Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 20:49:09 +0200 Subject: [PATCH 15/23] remove blank space --- examples/ed25519_cert.rs | 2 -- 1 file changed, 2 deletions(-) diff --git a/examples/ed25519_cert.rs b/examples/ed25519_cert.rs index a2d961e..27c2784 100644 --- a/examples/ed25519_cert.rs +++ b/examples/ed25519_cert.rs @@ -56,9 +56,7 @@ fn main() { &Capabilities::default_actor(), ) .unwrap(); - let data = csr.clone().to_der().unwrap(); - dbg!(&data); let file_name_with_extension = "cert.csr"; #[cfg(not(target_arch = "wasm32"))] std::fs::write(file_name_with_extension, &data).unwrap(); From 42364c2aa41f090997824e3eba2dcd4c2d1be637 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 20:49:25 +0200 Subject: [PATCH 16/23] add from_der example --- examples/ed25519_from_der.rs | 229 +++++++++++++++++++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 examples/ed25519_from_der.rs diff --git a/examples/ed25519_from_der.rs b/examples/ed25519_from_der.rs new file mode 100644 index 0000000..643e19c --- /dev/null +++ b/examples/ed25519_from_der.rs @@ -0,0 +1,229 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +#![allow(unused)] + +use std::str::FromStr; +use std::time::Duration; + +use der::asn1::{BitString, Ia5String, Uint, UtcTime}; +use der::{Decode, Encode}; +use ed25519_dalek::{Signature as Ed25519DalekSignature, Signer, SigningKey, VerifyingKey}; +use polyproto::certs::capabilities::Capabilities; +use polyproto::certs::idcert::IdCert; +use polyproto::certs::PublicKeyInfo; +use polyproto::key::{PrivateKey, PublicKey}; +use polyproto::signature::Signature; +use rand::rngs::OsRng; +use spki::{AlgorithmIdentifierOwned, ObjectIdentifier, SignatureBitStringEncoding}; +use thiserror::Error; +use x509_cert::attr::Attributes; +use x509_cert::name::RdnSequence; +use x509_cert::request::CertReq; +use x509_cert::time::{Time, Validity}; +use x509_cert::Certificate; + +/// The following example uses the same setup as in ed25519_basic.rs, but in its main method, it +/// creates a certificate signing request (CSR) and writes it to a file. The CSR is created from a +/// polyproto ID CSR, which is a wrapper around a PKCS #10 CSR. +/// +/// If you have openssl installed, you can inspect the CSR by running: +/// +/// ```sh +/// openssl req -in cert.csr -verify -inform der +/// ``` +/// +/// After that, the program creates an ID-Cert from the given ID-CSR. The `cert.der` file can also +/// be validated using openssl: +/// +/// ```sh +/// openssl x509 -in cert.der -text -noout -inform der +/// ``` + +#[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] +#[cfg_attr(not(target_arch = "wasm32"), test)] +fn main() { + let mut csprng = rand::rngs::OsRng; + let priv_key = Ed25519PrivateKey::gen_keypair(&mut csprng); + println!("Private Key is: {:?}", priv_key.key.to_bytes()); + println!("Public Key is: {:?}", priv_key.public_key.key.to_bytes()); + println!(); + + let csr = polyproto::certs::idcsr::IdCsr::new( + &RdnSequence::from_str("CN=flori,DC=www,DC=polyphony,DC=chat,UID=flori@polyphony.chat,uniqueIdentifier=client1").unwrap(), + &priv_key, + &Capabilities::default_actor(), + ) + .unwrap(); + + let data = csr.clone().to_der().unwrap(); + let file_name_with_extension = "cert.csr"; + #[cfg(not(target_arch = "wasm32"))] + std::fs::write(file_name_with_extension, &data).unwrap(); + + let cert = IdCert::from_actor_csr( + csr, + &priv_key, + Uint::new(&8932489u64.to_be_bytes()).unwrap(), + RdnSequence::from_str( + "CN=root,DC=www,DC=polyphony,DC=chat,UID=root@polyphony.chat,uniqueIdentifier=root", + ) + .unwrap(), + Validity { + not_before: Time::UtcTime( + UtcTime::from_unix_duration(Duration::from_secs(10)).unwrap(), + ), + not_after: Time::UtcTime( + UtcTime::from_unix_duration(Duration::from_secs(1000)).unwrap(), + ), + }, + ) + .unwrap(); + let data = cert.clone().to_der().unwrap(); + let cert_from_der = IdCert::from_der(data).unwrap(); + assert_eq!(cert_from_der, cert) +} + +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(test))] +fn main() {} + +// As mentioned in the README, we start by implementing the signature trait. + +// Here, we start by defining the signature type, which is a wrapper around the signature type from +// the ed25519-dalek crate. +#[derive(Debug, PartialEq, Eq, Clone)] +struct Ed25519Signature { + signature: Ed25519DalekSignature, + algorithm: AlgorithmIdentifierOwned, +} + +// We implement the Signature trait for our signature type. +impl Signature for Ed25519Signature { + // We define the signature type from the ed25519-dalek crate as the associated type. + type Signature = Ed25519DalekSignature; + + // This is straightforward: we return a reference to the signature. + fn as_signature(&self) -> &Self::Signature { + &self.signature + } + + // The algorithm identifier for a given signature implementation is constant. We just need + // to define it here. + fn algorithm_identifier() -> AlgorithmIdentifierOwned { + AlgorithmIdentifierOwned { + // This is the OID for Ed25519. It is defined in the IANA registry. + oid: ObjectIdentifier::from_str("1.3.101.112").unwrap(), + // For this example, we don't need or want any parameters. + parameters: None, + } + } + + fn from_bitstring(signature: &[u8]) -> Self { + let mut signature_vec = signature.to_vec(); + signature_vec.resize(64, 0); + let signature_array: [u8; 64] = { + let mut array = [0; 64]; + array.copy_from_slice(&signature_vec[..]); + array + }; + Self { + signature: Ed25519DalekSignature::from_bytes(&signature_array), + algorithm: Self::algorithm_identifier(), + } + } +} + +// The `SignatureBitStringEncoding` trait is used to convert a signature to a bit string. We implement +// it for our signature type. +impl SignatureBitStringEncoding for Ed25519Signature { + fn to_bitstring(&self) -> der::Result { + BitString::from_bytes(&self.as_signature().to_bytes()) + } +} + +// Next, we implement the key traits. We start by defining the private key type. +#[derive(Debug, Clone, PartialEq, Eq)] +struct Ed25519PrivateKey { + // Defined below + public_key: Ed25519PublicKey, + // The private key from the ed25519-dalek crate + key: SigningKey, +} + +impl PrivateKey for Ed25519PrivateKey { + type PublicKey = Ed25519PublicKey; + + // Return a reference to the public key + fn pubkey(&self) -> &Self::PublicKey { + &self.public_key + } + + // Signs a message. The beauty of having to wrap the ed25519-dalek crate is that we can + // harness all of its functionality, such as the `sign` method. + fn sign(&self, data: &[u8]) -> Ed25519Signature { + let signature = self.key.sign(data); + Ed25519Signature { + signature, + algorithm: self.algorithm_identifier(), + } + } +} + +impl Ed25519PrivateKey { + // Let's also define a handy method to generate a key pair. + pub fn gen_keypair(csprng: &mut OsRng) -> Self { + let key = SigningKey::generate(csprng); + let public_key = Ed25519PublicKey { + key: key.verifying_key(), + }; + Self { public_key, key } + } +} + +// Same thing as above for the public key type. +#[derive(Debug, Clone, PartialEq, Eq)] +struct Ed25519PublicKey { + // The public key type from the ed25519-dalek crate + key: VerifyingKey, +} + +impl PublicKey for Ed25519PublicKey { + // Verifies a signature. We use the `verify_strict` method from the ed25519-dalek crate. + // This method is used to mitigate weak key forgery. + fn verify_signature( + &self, + signature: &Ed25519Signature, + data: &[u8], + ) -> Result<(), polyproto::errors::composite::PublicKeyError> { + match self.key.verify_strict(data, signature.as_signature()) { + Ok(_) => Ok(()), + Err(_) => Err(polyproto::errors::composite::PublicKeyError::BadSignature), + } + } + + // Returns the public key info. Public key info is used to encode the public key in a + // certificate or a CSR. It is named after the `SubjectPublicKeyInfo` type from the X.509 + // standard, and thus includes the information needed to encode the public key in a certificate + // or a CSR. + fn public_key_info(&self) -> PublicKeyInfo { + PublicKeyInfo { + algorithm: Ed25519Signature::algorithm_identifier(), + public_key_bitstring: BitString::from_bytes(&self.key.to_bytes()).unwrap(), + } + } + + fn from_public_key_info(public_key_info: PublicKeyInfo) -> Self { + let mut key_vec = public_key_info.public_key_bitstring.raw_bytes().to_vec(); + key_vec.resize(32, 0); + let signature_array: [u8; 32] = { + let mut array = [0; 32]; + array.copy_from_slice(&key_vec[..]); + array + }; + Self { + key: VerifyingKey::from_bytes(&signature_array).unwrap(), + } + } +} From 20dfac5028c7e969a6ed094c253bdae1ddbc1f69 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 22:17:12 +0200 Subject: [PATCH 17/23] Replace most errors with unified error type, replace From with TryFrom where it made sense --- src/certs/capabilities/basic_constraints.rs | 104 +++++++++++--------- src/certs/capabilities/key_usage.rs | 88 +++++++++-------- src/certs/capabilities/mod.rs | 50 +++++----- src/certs/idcert.rs | 30 +++--- src/certs/idcerttbs.rs | 60 ++++++----- src/certs/idcsr.rs | 22 ++--- src/errors/base.rs | 34 ++----- src/errors/composite.rs | 35 +++++-- 8 files changed, 222 insertions(+), 201 deletions(-) diff --git a/src/certs/capabilities/basic_constraints.rs b/src/certs/capabilities/basic_constraints.rs index 5feb568..702ede2 100644 --- a/src/certs/capabilities/basic_constraints.rs +++ b/src/certs/capabilities/basic_constraints.rs @@ -11,6 +11,7 @@ use x509_cert::attr::Attribute; use x509_cert::ext::Extension; use crate::errors::base::{ConstraintError, InvalidInput}; +use crate::errors::composite::ConversionError; use super::OID_BASIC_CONSTRAINTS; @@ -36,7 +37,7 @@ impl From for ObjectIdentifier { } impl TryFrom for BasicConstraints { - type Error = InvalidInput; + type Error = ConversionError; /// Performs the conversion. /// /// Fails, if the input attribute @@ -51,21 +52,20 @@ impl TryFrom for BasicConstraints { fn try_from(value: Attribute) -> Result { // Basic input validation. Check OID of Attribute and length of the "values" SetOfVec provided. if value.oid.to_string() != super::OID_BASIC_CONSTRAINTS { - return Err(Self::Error::IncompatibleVariantForConversion { - reason: format!( + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + format!( "OID of value does not match any of OID_BASIC_CONSTRAINTS. Found OID {}", value.oid ), - }); + ))); } let values = value.values; if values.len() > 2usize { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: format!( - "Expected 1 or 2 values for BasicConstraints, found {}", - values.len() - ), - }); + return Err(ConversionError::InvalidInput(InvalidInput::Length { + min_length: 1, + max_length: 2, + actual_length: values.len().to_string(), + })); } let mut num_ca = 0u8; let mut num_path_length = 0u8; @@ -79,7 +79,7 @@ impl TryFrom for BasicConstraints { num_ca += 1; ca = any_to_bool(value.clone())?; } else { - return Err(InvalidInput::IncompatibleVariantForConversion { reason: "Encountered > 1 Boolean tags. Expected 1 Boolean tag.".to_string() }); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed("Encountered > 1 Boolean tags. Expected 1 Boolean tag.".to_string()))); } } Tag::Integer => { @@ -88,54 +88,57 @@ impl TryFrom for BasicConstraints { num_path_length += 1; path_length = Some(any_to_u64(value.clone())?); } else { - return Err(InvalidInput::IncompatibleVariantForConversion { reason: "Encountered > 1 Integer tags. Expected 0 or 1 Integer tags.".to_string() }); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed("Encountered > 1 Integer tags. Expected 0 or 1 Integer tags.".to_string()))); } } - _ => return Err(InvalidInput::IncompatibleVariantForConversion { reason: format!("Encountered unexpected tag {:?}, when tag should have been either Boolean or Integer", value.tag()) }), + _ => return Err(ConversionError::InvalidInput(InvalidInput::Malformed(format!("Encountered unexpected tag {:?}, when tag should have been either Boolean or Integer", value.tag())))), } } if num_ca == 0 { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: "Expected 1 Boolean tag, found 0".to_string(), - }); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + "Expected 1 Boolean tag, found 0".to_string(), + ))); } Ok(BasicConstraints { ca, path_length }) } } -impl From for Attribute { - fn from(value: BasicConstraints) -> Self { +impl TryFrom for Attribute { + type Error = ConversionError; + fn try_from(value: BasicConstraints) -> Result { let mut sov = SetOfVec::new(); - sov.insert(Any::new(der::Tag::Boolean, match value.ca { - true => vec![0xff], - false => vec![0x00], - }, - ).expect("Error occurred when converting BasicConstraints bool to der::Any. Please report this crash at https://github.com/polyphony-chat/polyproto.")).expect("Error occurred when inserting into der::Any to SetOfVec. Please report this crash at https://github.com/polyphony-chat/polyproto"); + sov.insert(Any::new( + der::Tag::Boolean, + match value.ca { + true => vec![0xff], + false => vec![0x00], + }, + )?)?; if let Some(length) = value.path_length { - sov.insert(Any::new(der::Tag::Integer, length.to_be_bytes()).expect("Error occurred when converting BasicConstraints u64 to der::Any. Please report this crash at https://github.com/polyphony-chat/polyproto.")). - expect("Error occurred when inserting into der::Any to SetOfVec. Please report this crash at https://github.com/polyphony-chat/polyproto"); + sov.insert(Any::new(der::Tag::Integer, length.to_be_bytes())?)?; } - Attribute { + Ok(Attribute { oid: value.into(), values: sov, - } + }) } } -impl From for Extension { - fn from(value: BasicConstraints) -> Self { - let attribute = Attribute::from(value); - Extension { +impl TryFrom for Extension { + type Error = ConversionError; + fn try_from(value: BasicConstraints) -> Result { + let attribute = Attribute::try_from(value)?; + Ok(Extension { extn_id: value.into(), critical: true, // This should be infallible. We are converting a boolean and a 64bit integer into DER and creating an OctetString from it. - extn_value: OctetString::new(attribute.values.to_der().expect("Error occurred when converting BasicConstraints u64 to DER. Please report this crash at https://github.com/polyphony-chat/polyproto.")).expect("Error occurred when converting BasicConstraints u64 to OctetString. Please report this crash at https://github.com/polyphony-chat/polyproto."), - } + extn_value: OctetString::new(attribute.values.to_der()?)?, + }) } } impl TryFrom for BasicConstraints { - type Error = InvalidInput; + type Error = ConversionError; /// Performs the conversion. Assumes, that the order of the bool value and the /// `int`/`none` value is **not** important. @@ -147,21 +150,18 @@ impl TryFrom for BasicConstraints { #[allow(unreachable_patterns)] if value.critical && !matches!(value.extn_id.to_string().as_str(), OID_BASIC_CONSTRAINTS) { // Error if we encounter a "critical" X.509 extension which we do not know of - return Err(InvalidInput::UnknownCriticalExtension { oid: value.extn_id }); + return Err(ConversionError::UnknownCriticalExtension { oid: value.extn_id }); } // If the Extension is a valid BasicConstraint, the octet string will contain DER ANY values // in a DER SET OF type - let sov: SetOfVec = match SetOfVec::from_der(value.extn_value.as_bytes()) { - Ok(sov) => sov, - Err(e) => return Err(InvalidInput::DerError(e)), - }; + let sov: SetOfVec = SetOfVec::from_der(value.extn_value.as_bytes())?; if sov.len() > 2 { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: format!( + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + format!( "This x509_cert::Extension has {} values stored. Expected a maximum of 2 values", sov.len() ), - }); + ))); } let mut bool_encounters = 0u8; let mut int_encounters = 0u8; @@ -182,10 +182,14 @@ impl TryFrom for BasicConstraints { null_encounters += 1; path_length = None; }, - _ => return Err(InvalidInput::IncompatibleVariantForConversion { reason: format!("Found {:?} in value, which does not match expected [Tag::Boolean, Tag::Integer, Tag::Null]", item.tag().to_string()) }), + _ => return Err(ConversionError::InvalidInput(InvalidInput::Malformed(format!("Encountered unexpected tag {:?}, when tag should have been either Boolean, Integer or Null", item.tag())))), } if bool_encounters > 1 || int_encounters > 1 || null_encounters > 1 { - return Err(InvalidInput::ConstraintError(ConstraintError::OutOfBounds { lower: 0, upper: 1, actual: 2.to_string(), reason: Some("Expected 0 or 1 Boolean, Integer or Null values, found more than one of one of these variants".to_string()) })); + return Err(ConversionError::InvalidInput(InvalidInput::Length { + min_length: 0, + max_length: 1, + actual_length: 2.to_string(), + })); } } Ok(BasicConstraints { ca, path_length }) @@ -225,6 +229,8 @@ fn any_to_u64(value: Any) -> Result { } } +#[cfg(test)] +#[allow(clippy::unwrap_used)] #[cfg(test)] mod test { use super::*; @@ -236,7 +242,7 @@ mod test { ca: true, path_length: Some(0u64), }; - let extension = Extension::from(basic_constraints); + let extension = Extension::try_from(basic_constraints).unwrap(); dbg!(extension); } @@ -247,7 +253,7 @@ mod test { ca: true, path_length: Some(u64::MAX), }; - let extension = Extension::from(basic_constraints); + let extension = Extension::try_from(basic_constraints).unwrap(); #[allow(clippy::unwrap_used)] let from_extension = BasicConstraints::try_from(extension).unwrap(); assert_eq!(from_extension, basic_constraints); @@ -256,7 +262,7 @@ mod test { ca: true, path_length: None, }; - let extension = Extension::from(basic_constraints); + let extension = Extension::try_from(basic_constraints).unwrap(); #[allow(clippy::unwrap_used)] let from_extension = BasicConstraints::try_from(extension).unwrap(); assert_eq!(from_extension, basic_constraints); @@ -265,7 +271,7 @@ mod test { ca: false, path_length: Some(u64::MAX), }; - let extension = Extension::from(basic_constraints); + let extension = Extension::try_from(basic_constraints).unwrap(); #[allow(clippy::unwrap_used)] let from_extension = BasicConstraints::try_from(extension).unwrap(); assert_eq!(from_extension, basic_constraints); @@ -274,7 +280,7 @@ mod test { ca: false, path_length: None, }; - let extension = Extension::from(basic_constraints); + let extension = Extension::try_from(basic_constraints).unwrap(); #[allow(clippy::unwrap_used)] let from_extension = BasicConstraints::try_from(extension).unwrap(); assert_eq!(from_extension, basic_constraints); diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 7b7072a..e5c1346 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -10,7 +10,8 @@ use spki::ObjectIdentifier; use x509_cert::attr::Attribute; use x509_cert::ext::Extension; -use crate::errors::base::{InvalidInput, UnsuccessfulConversion}; +use crate::errors::base::InvalidInput; +use crate::errors::composite::ConversionError; use super::*; @@ -57,13 +58,15 @@ pub enum KeyUsage { } impl TryFrom for KeyUsage { - type Error = UnsuccessfulConversion; + type Error = ConversionError; fn try_from(value: u32) -> Result { if value > 256 { - return Err(UnsuccessfulConversion::InvalidInput( - "Value too big to match this enums' variants".to_string(), - )); + return Err(ConversionError::InvalidInput(InvalidInput::Length { + min_length: 0, + max_length: 256, + actual_length: value.to_string(), + })); } match value { x if x == KeyUsage::DigitalSignature as u32 => Ok(KeyUsage::DigitalSignature), @@ -75,9 +78,9 @@ impl TryFrom for KeyUsage { x if x == KeyUsage::CrlSign as u32 => Ok(KeyUsage::CrlSign), x if x == KeyUsage::EncipherOnly as u32 => Ok(KeyUsage::EncipherOnly), x if x == KeyUsage::DecipherOnly as u32 => Ok(KeyUsage::DecipherOnly), - _ => Err(UnsuccessfulConversion::InvalidInput( - "Value does not match any of the variants".to_string(), - )), + _ => Err(ConversionError::InvalidInput(InvalidInput::Malformed( + "Input cannot be matched to any of the KeyUsage variants".to_string(), + ))), } } } @@ -114,7 +117,7 @@ impl KeyUsages { /// encipherOnly (7), /// decipherOnly (8) } /// ``` - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bytes(bytes: &[u8]) -> Result { /* Below, we are doing some operations on bits. RFC 5280 says: KeyUsage ::= BIT STRING { digitalSignature (0), @@ -214,26 +217,23 @@ impl From for BitString { } impl TryFrom for KeyUsages { - type Error = InvalidInput; + type Error = ConversionError; fn try_from(value: Attribute) -> Result { if value.tag() != Tag::BitString { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: format!("Expected BitString, found {}", value.tag()), - }); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + format!("Expected BitString, found {}", value.tag(),), + ))); } match value.values.len() { 0 => return Ok(KeyUsages::new(&[])), 1 => (), _ => { - return Err(InvalidInput::ConstraintError( - ConstraintError::OutOfBounds { - lower: 0, - upper: 1, - actual: value.values.len().to_string(), - reason: Some("Too many values to be a valid KeyUsages value".to_string()), - }, - )) + return Err(ConversionError::InvalidInput(InvalidInput::Length { + min_length: 0, + max_length: 1, + actual_length: value.values.len().to_string(), + })); } }; let inner_value = value.values.get(0).expect("Illegal state. Please report this error to https://github.com/polyphony-chat/polyproto"); @@ -248,16 +248,16 @@ fn divide_starting_number(number: &mut u32) { } impl TryFrom for KeyUsages { - type Error = InvalidInput; + type Error = ConversionError; fn try_from(value: Extension) -> Result { if value.extn_id.to_string().as_str() != OID_KEY_USAGE { - return Err(InvalidInput::IncompatibleVariantForConversion { - reason: format!( + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + format!( "Expected OID {} for KeyUsages, found OID {}", OID_KEY_USAGE, value.extn_id ), - }); + ))); } // here we need to NOT just do .as_bytes() but to somehow get the actual value in this // octetstring @@ -266,35 +266,37 @@ impl TryFrom for KeyUsages { } } -impl From for Attribute { - fn from(value: KeyUsages) -> Self { +impl TryFrom for Attribute { + type Error = ConversionError; + + fn try_from(value: KeyUsages) -> Result { let mut sov = SetOfVec::new(); let bitstring = value.to_bitstring(); - sov.insert( - Any::from_der(&bitstring.to_der().expect( - "Error occurred when converting blah blah blah this will be tryfrom anyways soon", - )) - .expect("this is tryfrom soon lalalalalla"), - ) - .expect("wow"); + sov.insert(Any::from_der(&bitstring.to_der()?)?) + .expect("wow"); dbg!(&sov); - Attribute { - oid: ObjectIdentifier::from_str(OID_KEY_USAGE).expect( - "Error occurred when converting KeyUsages to ObjectIdentifier. Please report this crash", - ), + Ok(Attribute { + oid: ObjectIdentifier::from_str(OID_KEY_USAGE)?, values: sov, - } + }) } } -impl From for Extension { - fn from(value: KeyUsages) -> Self { +impl TryFrom for Extension { + type Error = ConversionError; + fn try_from(value: KeyUsages) -> Result { let bitstring = value.to_bitstring(); - let any = Any::from_der(&bitstring.to_der().expect("guh")).expect("Error occurred when converting KeyUsages to BitString. Please report this crash at https://github.com/polyphony-chat/polyproto"); - Extension { extn_id: ObjectIdentifier::from_str(OID_KEY_USAGE).expect("Error occurred when converting KeyUsages to ObjectIdentifier. Please report this crash at https://github.com/polyphony-chat/polyproto"), critical: true, extn_value: OctetString::new(any.to_der().expect("Error occurred when converting KeyUsages to DER. Please report this crash at https://github.com/polyphony-chat/polyproto")).expect("Error occurred when creating OctetString. Please report this crash at https://github.com/polyphony-chat/polyproto") } + let any = Any::from_der(&bitstring.to_der()?)?; + Ok(Extension { + extn_id: ObjectIdentifier::from_str(OID_KEY_USAGE)?, + critical: true, + extn_value: OctetString::new(any.to_der()?)?, + }) } } +#[cfg(test)] +#[allow(clippy::unwrap_used)] #[cfg(test)] mod test { use super::*; diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index c70a3b6..e53d213 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -16,7 +16,8 @@ use x509_cert::attr::{Attribute, Attributes}; use x509_cert::ext::{Extension, Extensions}; use crate::errors::base::InvalidInput; -use crate::{Constrained, ConstraintError}; +use crate::errors::composite::ConversionError; +use crate::Constrained; /// Object Identifier for the KeyUsage::DigitalSignature variant. pub const OID_KEY_USAGE_DIGITAL_SIGNATURE: &str = "1.3.6.1.5.5.7.3.3"; @@ -98,7 +99,7 @@ impl Capabilities { } impl TryFrom for Capabilities { - type Error = InvalidInput; + type Error = ConversionError; /// Performs the conversion. /// @@ -119,7 +120,7 @@ impl TryFrom for Capabilities { OID_BASIC_CONSTRAINTS => { num_basic_constraints += 1; if num_basic_constraints > 1 { - return Err(InvalidInput::IncompatibleVariantForConversion { reason: "Tried inserting > 1 BasicConstraints into Capabilities. Expected 1 BasicConstraints".to_string() }); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed("Tried inserting > 1 BasicConstraints into Capabilities. Expected 1 BasicConstraints".to_string()))); } else { basic_constraints = BasicConstraints::try_from(item.clone())?; } @@ -135,40 +136,41 @@ impl TryFrom for Capabilities { } impl TryFrom for Attributes { + type Error = ConversionError; + /// Performs the conversion. /// /// Fails, if `Capabilities::verify()` using the `Constrained` trait fails. fn try_from(value: Capabilities) -> Result { value.validate()?; let mut sov = SetOfVec::new(); - let insertion = sov.insert(Attribute::from(value.key_usage)); + let insertion = sov.insert(Attribute::try_from(value.key_usage)?); if insertion.is_err() { - return Err(ConstraintError::Malformed(Some("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); } - let insertion = sov.insert(Attribute::from(value.basic_constraints)); + let insertion = sov.insert(Attribute::try_from(value.basic_constraints)?); if insertion.is_err() { - return Err(ConstraintError::Malformed(Some("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed("Tried inserting non-unique element into SetOfVec. You likely have a duplicate value in your Capabilities".to_string()))); } Ok(sov) } - - type Error = ConstraintError; } -impl From for Extensions { +impl TryFrom for Extensions { + type Error = ConversionError; /// Performs the conversion. /// /// try_from does **not** check whether the resulting [Extensions] are well-formed. - fn from(value: Capabilities) -> Self { - vec![ - Extension::from(value.basic_constraints), - Extension::from(value.key_usage), - ] + fn try_from(value: Capabilities) -> Result { + Ok(vec![ + Extension::try_from(value.basic_constraints)?, + Extension::try_from(value.key_usage)?, + ]) } } impl TryFrom for Capabilities { - type Error = InvalidInput; + type Error = ConversionError; /// Performs the conversion. /// @@ -188,7 +190,7 @@ impl TryFrom for Capabilities { dbg!("KeyUsage to Capabilities"); key_usage = KeyUsages::try_from(item.clone())? }, - _ => return Err(InvalidInput::ConstraintError(ConstraintError::Malformed(Some(format!("Invalid OID found for converting this set of Extensions to Capabilities: {} is not a valid OID for BasicConstraints or KeyUsages", item.extn_id))))) + _ => return Err(ConversionError::InvalidInput(InvalidInput::Malformed(format!("Invalid OID found for converting this set of Extensions to Capabilities: {} is not a valid OID for BasicConstraints or KeyUsages", item.extn_id)))) }; } Ok(Capabilities { @@ -198,6 +200,8 @@ impl TryFrom for Capabilities { } } +#[cfg(test)] +#[allow(clippy::unwrap_used)] #[cfg(test)] mod test { use spki::ObjectIdentifier; @@ -244,19 +248,19 @@ mod test { ca: false, path_length: None, }; - let _ = Attribute::from(bc); + let _ = Attribute::try_from(bc).unwrap(); bc.ca = true; - let _ = Attribute::from(bc); + let _ = Attribute::try_from(bc).unwrap(); bc.path_length = Some(0); - let _ = Attribute::from(bc); + let _ = Attribute::try_from(bc).unwrap(); // Why not test all sorts of values? :3 let mut county_count = 2u64; while county_count != u64::MAX { bc.path_length = Some(county_count); - let _ = Attribute::from(bc); + let _ = Attribute::try_from(bc).unwrap(); if let Some(res) = county_count.checked_mul(2) { county_count = res; } else { @@ -347,7 +351,7 @@ mod test_basic_constraints_from_attribute { ca: true, path_length: Some(0), }; - let attribute = Attribute::from(bc); + let attribute = Attribute::try_from(bc).unwrap(); let result = BasicConstraints::try_from(attribute); dbg!(&result); assert!(result.is_ok()); @@ -375,7 +379,7 @@ mod test_basic_constraints_from_attribute { ca: true, path_length: Some(0), }; - let mut attribute = Attribute::from(bc); + let mut attribute = Attribute::try_from(bc).unwrap(); attribute.oid = ObjectIdentifier::from_str("0.0.161.80085").unwrap(); let result = BasicConstraints::try_from(attribute); dbg!(&result); diff --git a/src/certs/idcert.rs b/src/certs/idcert.rs index 0db1489..b43cbd6 100644 --- a/src/certs/idcert.rs +++ b/src/certs/idcert.rs @@ -4,12 +4,12 @@ use der::asn1::Uint; use der::{Decode, Encode}; -use spki::AlgorithmIdentifierOwned; use x509_cert::name::Name; use x509_cert::time::Validity; use x509_cert::Certificate; -use crate::errors::composite::IdCertError; +use crate::errors::base::InvalidInput; +use crate::errors::composite::ConversionError; use crate::key::{PrivateKey, PublicKey}; use crate::signature::Signature; use crate::Constrained; @@ -50,16 +50,14 @@ impl> IdCert { serial_number: Uint, issuer: Name, validity: Validity, - ) -> Result { + ) -> Result { // IdCsr gets validated in IdCertTbs::from_..._csr let signature_algorithm = signing_key.algorithm_identifier(); issuer.validate()?; // TODO: Maybe this and the below validation should be done in IdCertTbs? if !equal_domain_components(&id_csr.inner_csr.subject, &issuer) { - return Err(IdCertError::ConstraintError( - crate::errors::base::ConstraintError::Malformed(Some( - "Domain components of the issuer and the subject do not match".to_string(), - )), - )); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + "Domain components of the issuer and the subject do not match".to_string(), + ))); } let id_cert_tbs = IdCertTbs::from_ca_csr(id_csr, serial_number, signature_algorithm, issuer, validity)?; @@ -84,15 +82,15 @@ impl> IdCert { serial_number: Uint, issuer: Name, validity: Validity, - ) -> Result { + ) -> Result { // IdCsr gets validated in IdCertTbs::from_..._csr let signature_algorithm = signing_key.algorithm_identifier(); issuer.validate()?; if !equal_domain_components(&id_csr.inner_csr.subject, &issuer) { - return Err(IdCertError::ConstraintError( - crate::errors::base::ConstraintError::Malformed(Some( + return Err(ConversionError::InvalidInput( + crate::errors::base::InvalidInput::Malformed( "Domain components of the issuer and the subject do not match".to_string(), - )), + ), )); } let id_cert_tbs = IdCertTbs::from_actor_csr( @@ -112,20 +110,20 @@ impl> IdCert { } /// Create an IdCsr from a byte slice containing a DER encoded X.509 Certificate. - pub fn from_der(value: Vec) -> Result { + pub fn from_der(value: Vec) -> Result { let cert = IdCert::try_from(Certificate::from_der(&value)?)?; cert.validate()?; Ok(cert) } /// Encode this type as DER, returning a byte vector. - pub fn to_der(self) -> Result, IdCertError> { + pub fn to_der(self) -> Result, ConversionError> { Ok(Certificate::try_from(self)?.to_der()?) } } impl> TryFrom> for Certificate { - type Error = IdCertError; + type Error = ConversionError; fn try_from(value: IdCert) -> Result { Ok(Self { tbs_certificate: value.id_cert_tbs.clone().try_into()?, @@ -136,7 +134,7 @@ impl> TryFrom> for Certificate { } impl> TryFrom for IdCert { - type Error = IdCertError; + type Error = ConversionError; fn try_from(value: Certificate) -> Result { let id_cert_tbs = value.tbs_certificate.try_into()?; diff --git a/src/certs/idcerttbs.rs b/src/certs/idcerttbs.rs index 797f4e7..b0e4810 100644 --- a/src/certs/idcerttbs.rs +++ b/src/certs/idcerttbs.rs @@ -12,7 +12,8 @@ use x509_cert::serial_number::SerialNumber; use x509_cert::time::Validity; use x509_cert::TbsCertificate; -use crate::errors::composite::{IdCertTbsError, IdCertToTbsCert, TbsCertToIdCert}; +use crate::errors::base::InvalidInput; +use crate::errors::composite::ConversionError; use crate::key::PublicKey; use crate::signature::Signature; use crate::Constrained; @@ -74,13 +75,11 @@ impl> IdCertTbs { signature_algorithm: AlgorithmIdentifierOwned, issuer: Name, validity: Validity, - ) -> Result { + ) -> Result { if id_csr.inner_csr.capabilities.basic_constraints.ca { - return Err(IdCertTbsError::ConstraintError( - crate::errors::base::ConstraintError::Malformed(Some( - "Actor ID-Cert cannot have \"CA\" BasicConstraint set to true".to_string(), - )), - )); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + "Actor ID-Cert cannot have \"CA\" BasicConstraint set to true".to_string(), + ))); } id_csr.validate()?; issuer.validate()?; @@ -113,13 +112,11 @@ impl> IdCertTbs { signature_algorithm: AlgorithmIdentifierOwned, issuer: Name, validity: Validity, - ) -> Result { + ) -> Result { if !id_csr.inner_csr.capabilities.basic_constraints.ca { - return Err(IdCertTbsError::ConstraintError( - crate::errors::base::ConstraintError::Malformed(Some( - "CA ID-Cert must have \"CA\" BasicConstraint set to true".to_string(), - )), - )); + return Err(ConversionError::InvalidInput(InvalidInput::Malformed( + "CA ID-Cert must have \"CA\" BasicConstraint set to true".to_string(), + ))); } id_csr.validate()?; // Verify if signature of IdCsr matches contents @@ -140,12 +137,12 @@ impl> IdCertTbs { } /// Encode this type as DER, returning a byte vector. - pub fn to_der(self) -> Result, IdCertTbsError> { + pub fn to_der(self) -> Result, ConversionError> { Ok(TbsCertificate::try_from(self)?.to_der()?) } /// Create an IdCsr from a byte slice containing a DER encoded PKCS #10 CSR. - pub fn from_der(bytes: &[u8]) -> Result { + pub fn from_der(bytes: &[u8]) -> Result { IdCertTbs::try_from(TbsCertificate::from_der(bytes)?) } } @@ -153,22 +150,25 @@ impl> IdCertTbs { impl> TryFrom> for IdCertTbs { - type Error = TbsCertToIdCert; + type Error = ConversionError; fn try_from(value: TbsCertificateInner

) -> Result { value.subject.validate()?; - let capabilities = match value.extensions { - Some(ext) => Capabilities::try_from(ext)?, - None => return Err(TbsCertToIdCert::Extensions), - }; + let capabilities = + match value.extensions { + Some(ext) => Capabilities::try_from(ext)?, + None => return Err(ConversionError::InvalidInput( + crate::errors::base::InvalidInput::Malformed( + "field 'extensions' was None. Expected: Some(x509_cert::ext::Extensions)" + .to_string(), + ), + )), + }; let subject_public_key_info = PublicKey::from_public_key_info(PublicKeyInfo::from(value.subject_public_key_info)); - let serial_number = match Uint::new(value.serial_number.as_bytes()) { - Ok(snum) => snum, - Err(e) => return Err(TbsCertToIdCert::Signature(e)), - }; + let serial_number = Uint::new(value.serial_number.as_bytes())?; Ok(Self { serial_number, @@ -186,12 +186,18 @@ impl> TryFrom> impl> TryFrom> for TbsCertificateInner

{ - type Error = IdCertToTbsCert; + type Error = ConversionError; fn try_from(value: IdCertTbs) -> Result { let serial_number = match SerialNumber::

::new(value.serial_number.as_bytes()) { Ok(sernum) => sernum, - Err(e) => return Err(IdCertToTbsCert::SerialNumber(e)), + Err(e) => { + return Err(ConversionError::InvalidInput( + crate::errors::base::InvalidInput::Malformed( + "Could not convert serial number".to_string(), + ), + )) + } }; let signature = AlgorithmIdentifierOwned { @@ -209,7 +215,7 @@ impl> TryFrom> subject_public_key_info: value.subject_public_key_info.public_key_info().into(), issuer_unique_id: None, subject_unique_id: None, - extensions: Some(Extensions::from(value.capabilities)), + extensions: Some(Extensions::try_from(value.capabilities)?), }) } } diff --git a/src/certs/idcsr.rs b/src/certs/idcsr.rs index 2845be1..fa478ba 100644 --- a/src/certs/idcsr.rs +++ b/src/certs/idcsr.rs @@ -10,7 +10,7 @@ use x509_cert::attr::Attributes; use x509_cert::name::Name; use x509_cert::request::{CertReq, CertReqInfo}; -use crate::errors::composite::{IdCsrError, IdCsrInnerError}; +use crate::errors::composite::ConversionError; use crate::key::{PrivateKey, PublicKey}; use crate::signature::Signature; use crate::{Constrained, ConstraintError}; @@ -62,7 +62,7 @@ impl> IdCsr { subject: &Name, signing_key: &impl PrivateKey, capabilities: &Capabilities, - ) -> Result, IdCsrError> { + ) -> Result, ConversionError> { subject.validate()?; let inner_csr = IdCsrInner::::new(subject, signing_key.pubkey(), capabilities)?; let signature = signing_key.sign(&inner_csr.clone().to_der()?); @@ -118,12 +118,12 @@ impl> IdCsr { } /// Create an IdCsr from a byte slice containing a DER encoded PKCS #10 CSR. - pub fn from_der(bytes: &[u8]) -> Result { + pub fn from_der(bytes: &[u8]) -> Result { IdCsr::try_from(CertReq::from_der(bytes)?) } /// Encode this type as DER, returning a byte vector. - pub fn to_der(self) -> Result, IdCsrError> { + pub fn to_der(self) -> Result, ConversionError> { Ok(CertReq::try_from(self)?.to_der()?) } } @@ -160,7 +160,7 @@ impl> IdCsrInner { subject: &Name, public_key: &P, capabilities: &Capabilities, - ) -> Result, IdCsrInnerError> { + ) -> Result, ConversionError> { subject.validate()?; capabilities.validate()?; @@ -177,18 +177,18 @@ impl> IdCsrInner { } /// Create an IdCsrInner from a byte slice containing a DER encoded PKCS #10 CSR. - pub fn from_der(bytes: &[u8]) -> Result { + pub fn from_der(bytes: &[u8]) -> Result { IdCsrInner::try_from(CertReqInfo::from_der(bytes)?) } /// Encode this type as DER, returning a byte vector. - pub fn to_der(self) -> Result, IdCsrInnerError> { + pub fn to_der(self) -> Result, ConversionError> { Ok(CertReqInfo::try_from(self)?.to_der()?) } } impl> TryFrom for IdCsr { - type Error = IdCsrError; + type Error = ConversionError; fn try_from(value: CertReq) -> Result { Ok(IdCsr { @@ -201,7 +201,7 @@ impl> TryFrom for IdCsr { } impl> TryFrom for IdCsrInner { - type Error = IdCsrInnerError; + type Error = ConversionError; fn try_from(value: CertReqInfo) -> Result { let rdn_sequence = value.subject; @@ -222,7 +222,7 @@ impl> TryFrom for IdCsrInner { } impl> TryFrom> for CertReq { - type Error = IdCsrError; + type Error = ConversionError; fn try_from(value: IdCsr) -> Result { Ok(CertReq { @@ -234,7 +234,7 @@ impl> TryFrom> for CertReq { } impl> TryFrom> for CertReqInfo { - type Error = IdCsrInnerError; + type Error = ConversionError; fn try_from(value: IdCsrInner) -> Result { Ok(CertReqInfo { version: x509_cert::request::Version::V1, diff --git a/src/errors/base.rs b/src/errors/base.rs index 30d49da..2e89bc2 100644 --- a/src/errors/base.rs +++ b/src/errors/base.rs @@ -2,7 +2,6 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. -use spki::ObjectIdentifier; use thiserror::Error; #[derive(Error, Debug, PartialEq, Clone)] @@ -21,29 +20,12 @@ pub enum ConstraintError { /// Represents errors for invalid input in IdCsr or IdCert generation. #[derive(Error, Debug, PartialEq, Clone)] pub enum InvalidInput { - #[error("The der library has reported the following error with the input")] - DerError(der::Error), - #[error("subject_session_id MUST NOT exceed length limit of 32 characters")] - SessionIdTooLong, - #[error( - "Cannot perform conversion, as input variant can not be converted to output. {reason:}" - )] - IncompatibleVariantForConversion { reason: String }, - #[error("Critical extension cannot be converted")] - UnknownCriticalExtension { oid: ObjectIdentifier }, - #[error(transparent)] - ConstraintError(#[from] ConstraintError), - #[error(transparent)] - UnsuccessfulConversion(#[from] UnsuccessfulConversion), -} - -#[derive(Error, Debug, PartialEq, Clone)] -// TODO: Replace usages of InvalidInput::IncompatibleVariantForConversion with this Enum -pub enum UnsuccessfulConversion { - #[error( - "Cannot perform conversion, as input variant can not be converted to output. {reason:}" - )] - IncompatibleVariant { reason: String }, - #[error("Conversion failed due to invalid input")] - InvalidInput(String), + #[error("The value is malformed and cannot be used as input: {0}")] + Malformed(String), + #[error("The value was expected to be between {min_length:?} and {max_length:?} but was {actual_length:?}")] + Length { + min_length: usize, + max_length: usize, + actual_length: String, + }, } diff --git a/src/errors/composite.rs b/src/errors/composite.rs index 356f6c9..6b8ad38 100644 --- a/src/errors/composite.rs +++ b/src/errors/composite.rs @@ -2,6 +2,7 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. +use spki::ObjectIdentifier; use thiserror::Error; use super::base::{ConstraintError, InvalidInput}; @@ -54,12 +55,6 @@ pub enum InvalidCert { InvalidCapabilities(ConstraintError), } -impl From for InvalidInput { - fn from(value: der::Error) -> Self { - Self::DerError(value) - } -} - #[derive(Error, Debug, PartialEq, Clone)] pub enum CertReqToIdCsr { #[error(transparent)] @@ -165,3 +160,31 @@ impl From for IdCertError { Self::DerError(value) } } + +#[derive(Error, Debug, PartialEq, Clone)] +pub enum ConversionError { + #[error(transparent)] + ConstraintError(#[from] ConstraintError), + #[error(transparent)] + InvalidInput(#[from] InvalidInput), + #[error("Encountered DER encoding error")] + DerError(der::Error), + #[error("Encountered DER OID error")] + ConstOidError(der::oid::Error), + #[error("Critical extension cannot be converted")] + UnknownCriticalExtension { oid: ObjectIdentifier }, + #[error(transparent)] + IdCertError(#[from] PublicKeyError), +} + +impl From for ConversionError { + fn from(value: der::Error) -> Self { + Self::DerError(value) + } +} + +impl From for ConversionError { + fn from(value: der::oid::Error) -> Self { + Self::ConstOidError(value) + } +} From 8992ba55dc3c1fc6d3402a4f8f7dffa2ae844783 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 22:21:02 +0200 Subject: [PATCH 18/23] Reformat error --- src/certs/idcerttbs.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/certs/idcerttbs.rs b/src/certs/idcerttbs.rs index b0e4810..bc03209 100644 --- a/src/certs/idcerttbs.rs +++ b/src/certs/idcerttbs.rs @@ -193,9 +193,10 @@ impl> TryFrom> Ok(sernum) => sernum, Err(e) => { return Err(ConversionError::InvalidInput( - crate::errors::base::InvalidInput::Malformed( - "Could not convert serial number".to_string(), - ), + crate::errors::base::InvalidInput::Malformed(format!( + "Could not convert serial number: {}", + e + )), )) } }; From ad649e68435f4d5e8aec4dc11cf94cedde71df82 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 22:21:59 +0200 Subject: [PATCH 19/23] Remove unneeded error types --- src/errors/composite.rs | 132 ---------------------------------------- 1 file changed, 132 deletions(-) diff --git a/src/errors/composite.rs b/src/errors/composite.rs index 6b8ad38..9172408 100644 --- a/src/errors/composite.rs +++ b/src/errors/composite.rs @@ -7,40 +7,6 @@ use thiserror::Error; use super::base::{ConstraintError, InvalidInput}; -/// Error type covering possible failures when converting a [x509_cert::TbsCertificate] -/// to a [crate::cert::IdCertTbs] -#[derive(Error, Debug, PartialEq, Clone)] -pub enum TbsCertToIdCert { - #[error("field 'subject_unique_id' was None. Expected: Some(der::asn1::BitString)")] - SubjectUid, - #[error("field 'extensions' was None. Expected: Some(x509_cert::ext::Extensions)")] - Extensions, - #[error("Supplied integer too long")] - Signature(der::Error), - #[error(transparent)] - ConstraintError(#[from] ConstraintError), - #[error(transparent)] - InvalidInput(#[from] InvalidInput), - #[error("Encounter DER encoding error")] - DerError(der::Error), -} - -impl From for TbsCertToIdCert { - fn from(value: der::Error) -> Self { - Self::DerError(value) - } -} - -/// Error type covering possible failures when converting a [crate::cert::IdCertTbs] -/// to a [x509_cert::TbsCertificate] -#[derive(Error, Debug, PartialEq, Clone)] -pub enum IdCertToTbsCert { - #[error("Serial number could not be converted")] - SerialNumber(der::Error), - #[error(transparent)] - ConstraintError(#[from] ConstraintError), -} - #[derive(Error, Debug, PartialEq, Clone)] pub enum InvalidCert { #[error("The signature does not match the contents of the certificate")] @@ -55,62 +21,6 @@ pub enum InvalidCert { InvalidCapabilities(ConstraintError), } -#[derive(Error, Debug, PartialEq, Clone)] -pub enum CertReqToIdCsr { - #[error(transparent)] - CertReqInfoToIdCsrInner(#[from] CertReqInfoError), -} - -#[derive(Error, Debug, PartialEq, Clone)] -pub enum CertReqInfoError { - #[error(transparent)] - ConstraintError(#[from] ConstraintError), - #[error(transparent)] - InvalidInput(#[from] InvalidInput), - #[error("Couldn't parse CertReqInfo from provided DER bytes")] - DerError(der::Error), - #[error(transparent)] - MalformedIdCsrInner(#[from] IdCsrInnerError), -} - -impl From for CertReqInfoError { - fn from(value: der::Error) -> Self { - Self::DerError(value) - } -} - -#[derive(Error, Debug, PartialEq, Clone)] -pub enum IdCsrError { - #[error("The Bitstring is invalid")] - InvalidBitstring(der::Error), - #[error(transparent)] - IdCsrInnerToCertReqInfo(#[from] IdCsrInnerError), - #[error(transparent)] - ConstraintError(#[from] ConstraintError), -} - -impl From for IdCsrError { - fn from(value: der::Error) -> Self { - Self::InvalidBitstring(value) - } -} - -#[derive(Error, Debug, PartialEq, Clone)] -pub enum IdCsrInnerError { - #[error(transparent)] - ConstraintError(#[from] ConstraintError), - #[error("Encountered DER encoding error")] - DerError(der::Error), - #[error(transparent)] - InvalidInput(#[from] InvalidInput), -} - -impl From for IdCsrInnerError { - fn from(value: der::Error) -> Self { - Self::DerError(value) - } -} - #[derive(Error, Debug, PartialEq, Hash, Clone)] pub enum PublicKeyError { #[error("The signature does not match the data")] @@ -119,48 +29,6 @@ pub enum PublicKeyError { BadPublicKeyInfo, } -#[derive(Error, Debug, PartialEq, Clone)] -pub enum IdCertTbsError { - #[error(transparent)] - PublicKeyError(#[from] PublicKeyError), - #[error(transparent)] - ConstraintError(#[from] ConstraintError), - #[error(transparent)] - IdCsrInnerError(#[from] IdCsrInnerError), - #[error(transparent)] - IdCertToTbsCert(#[from] IdCertToTbsCert), - #[error("Encountered DER encoding error")] - DerError(der::Error), -} - -impl From for IdCertTbsError { - fn from(value: der::Error) -> Self { - Self::DerError(value) - } -} - -#[derive(Error, Debug, PartialEq, Clone)] -pub enum IdCertError { - #[error(transparent)] - PublicKeyError(#[from] PublicKeyError), - #[error("Encountered DER encoding error")] - DerError(der::Error), - #[error(transparent)] - IdCertTbsError(#[from] IdCertTbsError), - #[error(transparent)] - IdCertToTbsCert(#[from] IdCertToTbsCert), - #[error(transparent)] - TbsCertToIdCert(#[from] TbsCertToIdCert), - #[error(transparent)] - ConstraintError(#[from] ConstraintError), -} - -impl From for IdCertError { - fn from(value: der::Error) -> Self { - Self::DerError(value) - } -} - #[derive(Error, Debug, PartialEq, Clone)] pub enum ConversionError { #[error(transparent)] From ede90b389a8e1d98888bfff140eab4013eb60451 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 22:24:52 +0200 Subject: [PATCH 20/23] Minor fixes --- src/certs/capabilities/key_usage.rs | 2 +- src/certs/capabilities/mod.rs | 2 +- src/certs/idcsr.rs | 1 - src/certs/mod.rs | 2 ++ src/constraints.rs | 2 +- src/errors/mod.rs | 1 - 6 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index e5c1346..0990da4 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -324,7 +324,7 @@ mod test { KeyUsage::DigitalSignature, ]; vec.sort(); - assert!(*vec.get(0).unwrap() == KeyUsage::DigitalSignature); + assert!(*vec.first().unwrap() == KeyUsage::DigitalSignature); assert!(*vec.get(1).unwrap() == KeyUsage::ContentCommitment); assert!(*vec.get(2).unwrap() == KeyUsage::DataEncipherment); assert!(*vec.get(3).unwrap() == KeyUsage::EncipherOnly); diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index e53d213..a17b912 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -214,7 +214,7 @@ mod test { todo!() } - fn test_key_usage_to_attribute(val: bool) { + fn test_key_usage_to_attribute(_val: bool) { // TODO todo!() } diff --git a/src/certs/idcsr.rs b/src/certs/idcsr.rs index fa478ba..e072b5a 100644 --- a/src/certs/idcsr.rs +++ b/src/certs/idcsr.rs @@ -194,7 +194,6 @@ impl> TryFrom for IdCsr { Ok(IdCsr { inner_csr: IdCsrInner::try_from(value.info)?, signature_algorithm: value.algorithm, - // TODO: raw_bytes() or as_bytes()? signature: S::from_bitstring(value.signature.raw_bytes()), }) } diff --git a/src/certs/mod.rs b/src/certs/mod.rs index 58ff84c..05377d1 100644 --- a/src/certs/mod.rs +++ b/src/certs/mod.rs @@ -112,6 +112,8 @@ impl From for SubjectPublicKeyInfoOwned { } } +/// Checks, if the domain components of two [Name]s are equal and ordered in the same way. Returns +/// `true`, if the domain components are equal, `false` otherwise. pub fn equal_domain_components(name_1: &Name, name_2: &Name) -> bool { let mut domain_components_1 = Vec::new(); let mut domain_components_2 = Vec::new(); diff --git a/src/constraints.rs b/src/constraints.rs index 7661655..db9a78b 100644 --- a/src/constraints.rs +++ b/src/constraints.rs @@ -5,7 +5,7 @@ use der::Length; use x509_cert::name::Name; -use crate::certs::capabilities::{Capabilities, KeyUsage, KeyUsages}; +use crate::certs::capabilities::{Capabilities, KeyUsage}; use crate::certs::idcert::IdCert; use crate::certs::idcerttbs::IdCertTbs; use crate::certs::idcsr::IdCsr; diff --git a/src/errors/mod.rs b/src/errors/mod.rs index 542f7ae..8edc686 100644 --- a/src/errors/mod.rs +++ b/src/errors/mod.rs @@ -7,7 +7,6 @@ pub mod base; /// "Composite" error types which consist of one or more "base" error types pub mod composite; -// TODO // PRETTYFYME // This module can be restructured to be a reflection of the src/ file tree. It would then be very // easy to tell, which file covers error types of which data types From 5fd79a7b75a8f171da5ee94f2953e623942f2677 Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 22:54:38 +0200 Subject: [PATCH 21/23] remove dbgs --- src/certs/capabilities/basic_constraints.rs | 3 +-- src/certs/capabilities/mod.rs | 10 ---------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/src/certs/capabilities/basic_constraints.rs b/src/certs/capabilities/basic_constraints.rs index 702ede2..36104b5 100644 --- a/src/certs/capabilities/basic_constraints.rs +++ b/src/certs/capabilities/basic_constraints.rs @@ -242,8 +242,7 @@ mod test { ca: true, path_length: Some(0u64), }; - let extension = Extension::try_from(basic_constraints).unwrap(); - dbg!(extension); + let _extension = Extension::try_from(basic_constraints).unwrap(); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index a17b912..dd60ab7 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -187,7 +187,6 @@ impl TryFrom for Capabilities { basic_constraints = BasicConstraints::try_from(item.clone())? } OID_KEY_USAGE => { - dbg!("KeyUsage to Capabilities"); key_usage = KeyUsages::try_from(item.clone())? }, _ => return Err(ConversionError::InvalidInput(InvalidInput::Malformed(format!("Invalid OID found for converting this set of Extensions to Capabilities: {} is not a valid OID for BasicConstraints or KeyUsages", item.extn_id)))) @@ -298,7 +297,6 @@ mod test_key_usage_from_attribute { values: sov, }; let result = KeyUsages::try_from(attribute); - dbg!(&result); assert!(result.is_err()); } @@ -313,7 +311,6 @@ mod test_key_usage_from_attribute { values: sov, }; let result = KeyUsages::try_from(attribute); - dbg!(&result); assert!(result.is_err()); } @@ -328,7 +325,6 @@ mod test_key_usage_from_attribute { values: sov, }; let result = KeyUsages::try_from(attribute); - dbg!(&result); assert!(result.is_err()); } } @@ -353,7 +349,6 @@ mod test_basic_constraints_from_attribute { }; let attribute = Attribute::try_from(bc).unwrap(); let result = BasicConstraints::try_from(attribute); - dbg!(&result); assert!(result.is_ok()); } @@ -368,7 +363,6 @@ mod test_basic_constraints_from_attribute { values: sov, }; let result = BasicConstraints::try_from(attribute); - dbg!(&result); assert!(result.is_err()); } @@ -382,7 +376,6 @@ mod test_basic_constraints_from_attribute { let mut attribute = Attribute::try_from(bc).unwrap(); attribute.oid = ObjectIdentifier::from_str("0.0.161.80085").unwrap(); let result = BasicConstraints::try_from(attribute); - dbg!(&result); assert!(result.is_err()); } @@ -399,7 +392,6 @@ mod test_basic_constraints_from_attribute { values: sov, }; let result = BasicConstraints::try_from(attribute); - dbg!(&result); assert!(result.is_err()); let mut sov = SetOfVec::new(); @@ -412,7 +404,6 @@ mod test_basic_constraints_from_attribute { values: sov, }; let result = BasicConstraints::try_from(attribute); - dbg!(&result); assert!(result.is_err()); } @@ -427,7 +418,6 @@ mod test_basic_constraints_from_attribute { values: sov, }; let result = BasicConstraints::try_from(attribute); - dbg!(&result); assert!(result.is_err()); } } From f4ea408c6c6485a6af3e167481dc014955694e5a Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 22:57:31 +0200 Subject: [PATCH 22/23] Rename from `from_bytes` to `from_bitstring`, fix `from_bitstring`, fix TryFrom for KeyUsages --- src/certs/capabilities/key_usage.rs | 56 ++++++++++++++--------------- 1 file changed, 26 insertions(+), 30 deletions(-) diff --git a/src/certs/capabilities/key_usage.rs b/src/certs/capabilities/key_usage.rs index 0990da4..c034def 100644 --- a/src/certs/capabilities/key_usage.rs +++ b/src/certs/capabilities/key_usage.rs @@ -117,7 +117,7 @@ impl KeyUsages { /// encipherOnly (7), /// decipherOnly (8) } /// ``` - pub fn from_bytes(bytes: &[u8]) -> Result { + pub fn from_bitstring(bitstring: BitString) -> Result { /* Below, we are doing some operations on bits. RFC 5280 says: KeyUsage ::= BIT STRING { digitalSignature (0), @@ -136,28 +136,29 @@ impl KeyUsages { Note, that BitStrings represent the bits they store in big endian order, meaning that the most significant bit (MSB) is the first bit in the BitString. */ - let bitstring = BitString::from_bytes(bytes)?; - // The "starting number" is 2 to the power of len(bitstring) [<-pseudocode]. - // For a bit of length 8, this would be 2^8=256. - let mut starting_number = 2u32.pow(bitstring.bit_len() as u32); + let mut starting_number = 1; let mut key_usages = Vec::new(); // We iterate over all bits, check if the current bit is set and try to convert the // current value of starting_number to a KeyUsage variant. On every iteration, we divide // starting_number by two, until it equals 1 and thus cannot be divided any further. - for bit in bitstring.bits() { - match bit { - true => 1u8, - false => { - divide_starting_number(&mut starting_number); - continue; - } - }; + for bit in bitstring.raw_bytes().iter() { + if *bit == 0 { + // If the bit is 0, we can skip the current iteration, increment the starting_number + // and continue with the next iteration. + multiply_starting_number(&mut starting_number); + continue; + } + if *bit != 1 { + // If the bit is not 0 or 1, we are likely looking at the "unused bits" byte of the + // BitString. We can safely ignore this byte. + continue; + } key_usages.push(KeyUsage::try_from(starting_number)?); - if starting_number == 1 { - // Stop the loop if starting_number is already 1. + if starting_number == 256 { + // Stop the loop if starting_number is already 256. break; } - divide_starting_number(&mut starting_number); + multiply_starting_number(&mut starting_number); } Ok(KeyUsages::new(&key_usages)) } @@ -206,7 +207,7 @@ impl KeyUsages { unused_bits += 1; } BitString::new(unused_bits, bytes) - .expect("Error when converting KeyUsages to BitString. Please report this error") + .expect("Error when converting KeyUsages to BitString. Please report this error to https://github.com/polyphony-chat/polyproto") } } @@ -237,13 +238,13 @@ impl TryFrom for KeyUsages { } }; let inner_value = value.values.get(0).expect("Illegal state. Please report this error to https://github.com/polyphony-chat/polyproto"); - KeyUsages::from_bytes(inner_value.value()) + KeyUsages::from_bitstring(BitString::from_der(inner_value.value())?) } } -fn divide_starting_number(number: &mut u32) { - if !*number == 1 { - *number /= 2; +fn multiply_starting_number(number: &mut u32) { + if !*number == 256 { + *number *= 2; } } @@ -259,10 +260,8 @@ impl TryFrom for KeyUsages { ), ))); } - // here we need to NOT just do .as_bytes() but to somehow get the actual value in this - // octetstring - // TODO - KeyUsages::from_bytes(value.extn_value.as_bytes()) + let any = Any::from_der(value.extn_value.as_bytes())?; + KeyUsages::from_bitstring(BitString::from_bytes(any.value())?) } } @@ -272,9 +271,7 @@ impl TryFrom for Attribute { fn try_from(value: KeyUsages) -> Result { let mut sov = SetOfVec::new(); let bitstring = value.to_bitstring(); - sov.insert(Any::from_der(&bitstring.to_der()?)?) - .expect("wow"); - dbg!(&sov); + sov.insert(Any::from_der(&bitstring.to_der()?)?)?; Ok(Attribute { oid: ObjectIdentifier::from_str(OID_KEY_USAGE)?, values: sov, @@ -309,8 +306,7 @@ mod test { KeyUsage::EncipherOnly, KeyUsage::KeyAgreement, ]); - let bitstring = BitString::from(key_usages); - dbg!(bitstring); + let _bitstring = BitString::from(key_usages); } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] From 99c959d31f6224e54572ea8fc8cb8b26bdf41fba Mon Sep 17 00:00:00 2001 From: bitfl0wer Date: Sun, 14 Apr 2024 23:02:31 +0200 Subject: [PATCH 23/23] Remove unnecessary tests --- src/certs/capabilities/mod.rs | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/src/certs/capabilities/mod.rs b/src/certs/capabilities/mod.rs index dd60ab7..2cc490c 100644 --- a/src/certs/capabilities/mod.rs +++ b/src/certs/capabilities/mod.rs @@ -206,29 +206,6 @@ mod test { use spki::ObjectIdentifier; use super::*; - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg_attr(not(target_arch = "wasm32"), test)] - fn test_key_usage_to_object_identifier() { - // TODO - todo!() - } - - fn test_key_usage_to_attribute(_val: bool) { - // TODO - todo!() - } - - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg_attr(not(target_arch = "wasm32"), test)] - fn test_key_usage_to_attribute_true() { - test_key_usage_to_attribute(true); - } - - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg_attr(not(target_arch = "wasm32"), test)] - fn test_key_usage_to_attribute_false() { - test_key_usage_to_attribute(false); - } #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] @@ -279,13 +256,6 @@ mod test_key_usage_from_attribute { use super::*; - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] - #[cfg_attr(not(target_arch = "wasm32"), test)] - fn test_key_usage_from_attribute() { - // TODO - todo!() - } - #[cfg_attr(target_arch = "wasm32", wasm_bindgen_test::wasm_bindgen_test)] #[cfg_attr(not(target_arch = "wasm32"), test)] fn test_key_usage_tag_mismatch() {