diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_algo_identifier.rs b/catalyst-gateway-crates/c509-certificate/src/c509_algo_identifier.rs new file mode 100644 index 00000000000..c252bae48c3 --- /dev/null +++ b/catalyst-gateway-crates/c509-certificate/src/c509_algo_identifier.rs @@ -0,0 +1,87 @@ +//! C509 Algorithm Identifier +//! This module handle the `AlgorithmIdentifier` type where OID does not fall into the +//! table. +//! +//! ```cddl +//! AlgorithmIdentifier = int / ~oid / [ algorithm: ~oid, parameters: bytes ] +//! ``` +//! +//! For more information about `AlgorithmIdentifier`, +//! visit [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) + +use asn1_rs::Oid; +use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; + +use crate::c509_oid::C509oid; + +/// A struct represents the `AlgorithmIdentifier` type. +#[derive(Debug, Clone, PartialEq)] +pub struct AlgorithmIdentifier { + /// A `C509oid` + oid: C509oid, + /// An optional parameter string + param: Option, +} + +impl AlgorithmIdentifier { + /// Create new instance of `AlgorithmIdentifier`. + #[must_use] + pub fn new(oid: Oid<'static>, param: Option) -> Self { + Self { + oid: C509oid::new(oid), + param, + } + } + + /// Get the OID. + pub(crate) fn get_oid(&self) -> Oid<'static> { + self.oid.clone().get_oid() + } + + /// Get the parameter. + pub(crate) fn get_param(&self) -> &Option { + &self.param + } +} + +impl Encode<()> for AlgorithmIdentifier { + fn encode( + &self, e: &mut Encoder, ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + match &self.param { + // [ algorithm: ~oid, parameters: bytes ] + Some(p) => { + e.array(2)?; + self.oid.encode(e, ctx)?; + e.bytes(p.as_bytes())?; + }, + // ~oid + None => { + self.oid.encode(e, ctx)?; + }, + } + Ok(()) + } +} + +impl Decode<'_, ()> for AlgorithmIdentifier { + fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { + // [ algorithm: ~oid, parameters: bytes ] + if d.datatype()? == minicbor::data::Type::Array { + let len = d.array()?.ok_or(minicbor::decode::Error::message( + "Failed to get array length", + ))?; + if len != 2 { + return Err(minicbor::decode::Error::message("Array length must be 2")); + } + let c509_oid = C509oid::decode(d, ctx)?; + let param = + String::from_utf8(d.bytes()?.to_vec()).map_err(minicbor::decode::Error::message)?; + Ok(AlgorithmIdentifier::new(c509_oid.get_oid(), Some(param))) + // ~oid + } else { + let oid = C509oid::decode(d, ctx)?; + Ok(AlgorithmIdentifier::new(oid.get_oid(), None)) + } + } +} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/data.rs b/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/data.rs new file mode 100644 index 00000000000..347e93e50a3 --- /dev/null +++ b/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/data.rs @@ -0,0 +1,82 @@ +//! Signature algorithm data provides a necessary information for encoding and decoding of +//! C509 `issuerSignatureAlgorithm`. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) +//! Section 9.10 C509 Signature Algorithms Registry for more information. + +// cspell: words RSASSA XMSS + +use anyhow::Error; +use asn1_rs::{oid, Oid}; +use once_cell::sync::Lazy; + +use crate::tables::IntegerToOidTable; + +/// Type of algorithm data +/// INT | OID | Name +type AlgorithmDataTuple = (i16, Oid<'static>, &'static str); + +/// Signature algorithm data table. +#[rustfmt::skip] +const SIG_ALGO_DATA: [AlgorithmDataTuple; 24] = [ + // Int | OID | Name + (-256, oid!(1.2.840.113549.1.1.5), "RSASSA-PKCS1-v1_5 with SHA-1"), + (-255, oid!(1.2.840.10045.4.1), "ECDSA with SHA-1"), + (0, oid!(1.2.840.10045.4.3.2), "ECDSA with SHA-256"), + (1, oid!(1.2.840.10045.4.3.3), "ECDSA with SHA-384"), + (2, oid!(1.2.840.10045.4.3.4), "ECDSA with SHA-512"), + (3, oid!(1.3.6.1.5.5.7.6.32), "ECDSA with SHAKE128"), + (4, oid!(1.3.6.1.5.5.7.6.33), "ECDSA with SHAKE256"), + (12, oid!(1.3.101.112), "Ed25519"), + (13, oid!(1.3.101.113), "Ed448"), + (14, oid!(1.3.6.1.5.5.7.6.26), "SHA-256 with HMAC-SHA256"), + (15, oid!(1.3.6.1.5.5.7.6.27), "SHA-384 with HMAC-SHA384"), + (16, oid!(1.3.6.1.5.5.7.6.28), "SHA-512 with HMAC-SHA512"), + (23, oid!(1.2.840.113549.1.1.11), "RSASSA-PKCS1-v1_5 with SHA-256"), + (24, oid!(1.2.840.113549.1.1.12), "RSASSA-PKCS1-v1_5 with SHA-384"), + (25, oid!(1.2.840.113549.1.1.13), "RSASSA-PKCS1-v1_5 with SHA-512"), + (26, oid!(1.2.840.113549.1.1.10), "RSASSA-PSS with SHA-256"), + (27, oid!(1.2.840.113549.1.1.10), "RSASSA-PSS with SHA-384"), + (28, oid!(1.2.840.113549.1.1.10), "RSASSA-PSS with SHA-512"), + (29, oid!(1.3.6.1.5.5.7.6.30), "RSASSA-PSS with SHAKE128"), + (30, oid!(1.3.6.1.5.5.7.6.3), "RSASSA-PSS with SHAKE256"), + (42, oid!(1.2.840.113549.1.9.16.3.17), "HSS / LMS"), + (43, oid!(0.4.0.127.0.15.1.1.13.0), "XMSS"), + (44, oid!(0.4.0.127.0.15.1.1.14.0), "XMSS^MT"), + (45, oid!(1.2.156.10197.1.501), "SM2 with SM3"), +]; + +/// A struct of data that contains lookup table of integer to OID in +/// bidirectional way for `IssuerSignatureAlgorithm`. +pub(crate) struct IssuerSigAlgoData(IntegerToOidTable); + +impl IssuerSigAlgoData { + /// Get the `IntegerToOidTable` + pub(crate) fn get_int_to_oid_table(&self) -> &IntegerToOidTable { + &self.0 + } +} + +/// Define static lookup for issuer signature algorithm table +static ISSUER_SIG_ALGO_TABLE: Lazy = Lazy::new(|| { + let mut int_to_oid_table = IntegerToOidTable::new(); + + for data in SIG_ALGO_DATA { + int_to_oid_table.add(data.0, data.1); + } + + IssuerSigAlgoData(int_to_oid_table) +}); + +/// Static reference to the `IssuerSigAlgoData` lookup table. +pub(crate) static ISSUER_SIG_ALGO_LOOKUP: &Lazy = &ISSUER_SIG_ALGO_TABLE; + +/// Get the OID from the int value. +pub(crate) fn get_oid_from_int(i: i16) -> Result, Error> { + ISSUER_SIG_ALGO_TABLE + .get_int_to_oid_table() + .get_map() + .get_by_left(&i) + .ok_or(Error::msg(format!( + "OID not found in the signature algorithms registry table given int {i}" + ))) + .cloned() +} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/mod.rs new file mode 100644 index 00000000000..cd4203b8b5e --- /dev/null +++ b/catalyst-gateway-crates/c509-certificate/src/c509_issuer_sig_algo/mod.rs @@ -0,0 +1,144 @@ +//! C509 Issuer Signature Algorithm as a part of `TBSCertificate` used in C509 +//! Certificate. +//! +//! ```cddl +//! issuerSignatureAlgorithm: AlgorithmIdentifier +//! ``` + +mod data; +use asn1_rs::Oid; +use data::{get_oid_from_int, ISSUER_SIG_ALGO_LOOKUP}; +use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; + +use crate::{c509_algo_identifier::AlgorithmIdentifier, c509_oid::C509oidRegistered}; + +/// A struct represents the `IssuerSignatureAlgorithm` +#[derive(Debug, Clone, PartialEq)] +pub struct IssuerSignatureAlgorithm { + /// The registered OID of the `IssuerSignatureAlgorithm`. + registered_oid: C509oidRegistered, + /// An `AlgorithmIdentifier` type + algo_identifier: AlgorithmIdentifier, +} + +impl IssuerSignatureAlgorithm { + /// Create new instance of `IssuerSignatureAlgorithm` where it registered with + /// Issuer Signature Algorithm lookup table. + pub fn new(oid: Oid<'static>, param: Option) -> Self { + Self { + registered_oid: C509oidRegistered::new( + oid.clone(), + ISSUER_SIG_ALGO_LOOKUP.get_int_to_oid_table(), + ), + algo_identifier: AlgorithmIdentifier::new(oid, param), + } + } +} + +impl Encode<()> for IssuerSignatureAlgorithm { + fn encode( + &self, e: &mut Encoder, ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + if let Some(&i) = self + .registered_oid + .get_table() + .get_map() + .get_by_right(&self.registered_oid.get_c509_oid().get_oid()) + { + e.i16(i)?; + } else { + AlgorithmIdentifier::encode(&self.algo_identifier, e, ctx)?; + } + Ok(()) + } +} + +impl Decode<'_, ()> for IssuerSignatureAlgorithm { + fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { + match d.datatype()? { + // Check i16 for -256 and -256 + minicbor::data::Type::U8 | minicbor::data::Type::I16 => { + let i = d.i16()?; + let oid = get_oid_from_int(i).map_err(minicbor::decode::Error::message)?; + Ok(Self::new(oid, None)) + }, + _ => { + let algo_identifier = AlgorithmIdentifier::decode(d, ctx)?; + Ok(IssuerSignatureAlgorithm::new( + algo_identifier.get_oid(), + algo_identifier.get_param().clone(), + )) + }, + } + } +} + +// ------------------Test---------------------- + +#[cfg(test)] +mod test_issuer_signature_algorithm { + use asn1_rs::oid; + + use super::*; + + #[test] + fn test_registered_oid() { + let mut buffer = Vec::new(); + let mut encoder = Encoder::new(&mut buffer); + + let isa = IssuerSignatureAlgorithm::new(oid!(1.3.101 .112), None); + isa.encode(&mut encoder, &mut ()) + .expect("Failed to encode IssuerSignatureAlgorithm"); + + // Ed25519 - int 12: 0x0c + assert_eq!(hex::encode(buffer.clone()), "0c"); + + let mut decoder = Decoder::new(&buffer); + let decoded_isa = IssuerSignatureAlgorithm::decode(&mut decoder, &mut ()) + .expect("Failed to decode IssuerSignatureAlgorithm"); + assert_eq!(decoded_isa, isa); + } + + #[test] + fn test_unregistered_oid() { + let mut buffer = Vec::new(); + let mut encoder = Encoder::new(&mut buffer); + + let isa = IssuerSignatureAlgorithm::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1), None); + isa.encode(&mut encoder, &mut ()) + .expect("Failed to encode IssuerSignatureAlgorithm"); + + // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 + assert_eq!(hex::encode(buffer.clone()), "49608648016503040201"); + + let mut decoder = Decoder::new(&buffer); + let decoded_isa = IssuerSignatureAlgorithm::decode(&mut decoder, &mut ()) + .expect("Failed to decode IssuerSignatureAlgorithm"); + assert_eq!(decoded_isa, isa); + } + + #[test] + fn test_unregistered_oid_with_param() { + let mut buffer = Vec::new(); + let mut encoder = Encoder::new(&mut buffer); + + let isa = IssuerSignatureAlgorithm::new( + oid!(2.16.840 .1 .101 .3 .4 .2 .1), + Some("example".to_string()), + ); + isa.encode(&mut encoder, &mut ()) + .expect("Failed to encode IssuerSignatureAlgorithm"); + // Array of 2 items: 0x82 + // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 + // bytes "example": 0x476578616d706c65 + assert_eq!( + hex::encode(buffer.clone()), + "8249608648016503040201476578616d706c65" + ); + + let mut decoder = Decoder::new(&buffer); + let decoded_isa = IssuerSignatureAlgorithm::decode(&mut decoder, &mut ()) + .expect("Failed to decode IssuerSignatureAlgorithm"); + assert_eq!(decoded_isa, isa); + } +} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/data.rs b/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/data.rs new file mode 100644 index 00000000000..33a70b23e19 --- /dev/null +++ b/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/data.rs @@ -0,0 +1,75 @@ +//! Public key algorithm data provides a necessary information for encoding and decoding +//! of C509 `subjectPublicKeyAlgorithm`. See [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) +//! Section 9.11 C509 Public Key Algorithms Registry for more information. + +// cspell: words Weierstraß secp XMSS brainpool + +use anyhow::Error; +use asn1_rs::{oid, Oid}; +use once_cell::sync::Lazy; + +use crate::tables::IntegerToOidTable; + +/// Type of algorithm data +/// INT | OID | Name +type AlgorithmDataTuple = (i16, Oid<'static>, &'static str); + +/// Public key algorithm data table. +#[rustfmt::skip] +const PUB_KEY_ALGO_DATA: [AlgorithmDataTuple; 16] = [ + // Int | OID | Name + (0, oid!(1.2.840.113549.1.1.1), "RSA"), + (1, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with secp256r1"), + (2, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with secp384r1"), + (3, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with secp521r1"), + (8, oid!(1.3.101.110), "X25519 (Montgomery)"), + (9, oid!(1.3.101.111), "X448 (Montgomery)"), + (10, oid!(1.3.101.112), "Ed25519 (Twisted Edwards)"), + (11, oid!(1.3.101.113), "Ed448 (Edwards)"), + (16, oid!(1.2.840.113549.1.9.16.3.17), "HSS / LMS"), + (17, oid!(0.4.0.127.0.15.1.1.13.0), "XMSS"), + (18, oid!(0.4.0.127.0.15.1.1.14.0), "XMSS^MT"), + (24, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with brainpoolP256r1"), + (25, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with brainpoolP384r1"), + (26, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with brainpoolP512r1"), + (27, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with FRP256v1"), + (28, oid!(1.2.840.10045.2.1), "EC Public Key (Weierstraß) with sm2p256v1"), +]; + +/// A struct of data that contains lookup table of integer to OID in +/// bidirectional way for `SubjectPublicKeyAlgorithm`. +pub(crate) struct SubjectPubKeyAlgoData(IntegerToOidTable); + +impl SubjectPubKeyAlgoData { + /// Get the `IntegerToOidTable` + pub(crate) fn get_int_to_oid_table(&self) -> &IntegerToOidTable { + &self.0 + } +} + +/// Define static lookup for subject publickey table +static SUBJECT_PUB_KEY_ALGO_TABLE: Lazy = Lazy::new(|| { + let mut int_to_oid_table = IntegerToOidTable::new(); + + for data in PUB_KEY_ALGO_DATA { + int_to_oid_table.add(data.0, data.1); + } + + SubjectPubKeyAlgoData(int_to_oid_table) +}); + +/// Static reference to the `SubjectPubKeyAlgoData` lookup table. +pub(crate) static SUBJECT_PUB_KEY_ALGO_LOOKUP: &Lazy = + &SUBJECT_PUB_KEY_ALGO_TABLE; + +/// Get the OID from the int value. +pub(crate) fn get_oid_from_int(i: i16) -> Result, Error> { + SUBJECT_PUB_KEY_ALGO_TABLE + .get_int_to_oid_table() + .get_map() + .get_by_left(&i) + .ok_or(Error::msg(format!( + "OID not found in the public key algorithms registry table given int {i}" + ))) + .cloned() +} diff --git a/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/mod.rs b/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/mod.rs new file mode 100644 index 00000000000..c7346e08beb --- /dev/null +++ b/catalyst-gateway-crates/c509-certificate/src/c509_subject_pub_key_algo/mod.rs @@ -0,0 +1,143 @@ +//! C509 Issuer Signature Algorithm as a part of `TBSCertificate` used in C509 +//! Certificate. +//! +//! ```cddl +//! subjectPublicKeyAlgorithm: AlgorithmIdentifier +//! ``` + +// cspell: words spka + +mod data; +use asn1_rs::Oid; +use data::{get_oid_from_int, SUBJECT_PUB_KEY_ALGO_LOOKUP}; +use minicbor::{encode::Write, Decode, Decoder, Encode, Encoder}; + +use crate::{c509_algo_identifier::AlgorithmIdentifier, c509_oid::C509oidRegistered}; + +/// A struct represents the `SubjectPubKeyAlgorithm` +#[derive(Debug, Clone, PartialEq)] +pub struct SubjectPubKeyAlgorithm { + /// The registered OID of the `SubjectPubKeyAlgorithm`. + registered_oid: C509oidRegistered, + /// An `AlgorithmIdentifier` type + algo_identifier: AlgorithmIdentifier, +} + +impl SubjectPubKeyAlgorithm { + /// Create new instance of `SubjectPubKeyAlgorithm` where it registered with + /// Subject Public Key Algorithm lookup table. + pub fn new(oid: Oid<'static>, param: Option) -> Self { + Self { + registered_oid: C509oidRegistered::new( + oid.clone(), + SUBJECT_PUB_KEY_ALGO_LOOKUP.get_int_to_oid_table(), + ), + algo_identifier: AlgorithmIdentifier::new(oid, param), + } + } +} + +impl Encode<()> for SubjectPubKeyAlgorithm { + fn encode( + &self, e: &mut Encoder, ctx: &mut (), + ) -> Result<(), minicbor::encode::Error> { + if let Some(&i) = self + .registered_oid + .get_table() + .get_map() + .get_by_right(&self.registered_oid.get_c509_oid().get_oid()) + { + e.i16(i)?; + } else { + AlgorithmIdentifier::encode(&self.algo_identifier, e, ctx)?; + } + Ok(()) + } +} + +impl Decode<'_, ()> for SubjectPubKeyAlgorithm { + fn decode(d: &mut Decoder<'_>, ctx: &mut ()) -> Result { + // Check u8 for 0 - 28 + if d.datatype()? == minicbor::data::Type::U8 { + let i = d.i16()?; + let oid = get_oid_from_int(i).map_err(minicbor::decode::Error::message)?; + Ok(Self::new(oid, None)) + } else { + let algo_identifier = AlgorithmIdentifier::decode(d, ctx)?; + Ok(SubjectPubKeyAlgorithm::new( + algo_identifier.get_oid(), + algo_identifier.get_param().clone(), + )) + } + } +} + +// ------------------Test---------------------- + +#[cfg(test)] +mod test_subject_public_key_algorithm { + use asn1_rs::oid; + + use super::*; + + #[test] + fn test_registered_oid() { + let mut buffer = Vec::new(); + let mut encoder = Encoder::new(&mut buffer); + + let spka = SubjectPubKeyAlgorithm::new(oid!(1.3.101 .112), None); + spka.encode(&mut encoder, &mut ()) + .expect("Failed to encode SubjectPubKeyAlgorithm"); + + // Ed25519 - int 10: 0x0a + assert_eq!(hex::encode(buffer.clone()), "0a"); + + let mut decoder = Decoder::new(&buffer); + let decoded_spka = SubjectPubKeyAlgorithm::decode(&mut decoder, &mut ()) + .expect("Failed to decode SubjectPubKeyAlgorithm"); + assert_eq!(decoded_spka, spka); + } + + #[test] + fn test_unregistered_oid() { + let mut buffer = Vec::new(); + let mut encoder = Encoder::new(&mut buffer); + + let spka = SubjectPubKeyAlgorithm::new(oid!(2.16.840 .1 .101 .3 .4 .2 .1), None); + spka.encode(&mut encoder, &mut ()) + .expect("Failed to encode SubjectPubKeyAlgorithm"); + + // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 + assert_eq!(hex::encode(buffer.clone()), "49608648016503040201"); + + let mut decoder = Decoder::new(&buffer); + let decoded_spka = SubjectPubKeyAlgorithm::decode(&mut decoder, &mut ()) + .expect("Failed to decode SubjectPubKeyAlgorithm"); + assert_eq!(decoded_spka, spka); + } + + #[test] + fn test_unregistered_oid_with_param() { + let mut buffer = Vec::new(); + let mut encoder = Encoder::new(&mut buffer); + + let spka = SubjectPubKeyAlgorithm::new( + oid!(2.16.840 .1 .101 .3 .4 .2 .1), + Some("example".to_string()), + ); + spka.encode(&mut encoder, &mut ()) + .expect("Failed to encode SubjectPubKeyAlgorithm"); + // Array of 2 items: 0x82 + // 2.16.840 .1 .101 .3 .4 .2 .1: 0x49608648016503040201 + // bytes "example": 0x476578616d706c65 + assert_eq!( + hex::encode(buffer.clone()), + "8249608648016503040201476578616d706c65" + ); + + let mut decoder = Decoder::new(&buffer); + let decoded_spka = SubjectPubKeyAlgorithm::decode(&mut decoder, &mut ()) + .expect("Failed to decode SubjectPubKeyAlgorithm"); + assert_eq!(decoded_spka, spka); + } +} diff --git a/catalyst-gateway-crates/c509-certificate/src/lib.rs b/catalyst-gateway-crates/c509-certificate/src/lib.rs index 41a76e0be34..ca6321fb725 100644 --- a/catalyst-gateway-crates/c509-certificate/src/lib.rs +++ b/catalyst-gateway-crates/c509-certificate/src/lib.rs @@ -6,11 +6,14 @@ //! Please refer to the [C509 Certificate](https://datatracker.ietf.org/doc/draft-ietf-cose-cbor-encoded-cert/09/) for more information. use wasm_bindgen::prelude::wasm_bindgen; +pub mod c509_algo_identifier; pub mod c509_attributes; pub mod c509_extensions; pub mod c509_general_names; +pub mod c509_issuer_sig_algo; pub mod c509_name; pub mod c509_oid; +pub mod c509_subject_pub_key_algo; mod tables; /// Generates an unsigned C509 certificate from the provided `TbsCertificate`.