From fe7367bf1beabfff8af9dacb9bfb7bc6c35f958f Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:33:41 -0400 Subject: [PATCH 01/24] cms: ecc-kari support - add kari and utils modules --- cms/src/builder.rs | 4 ++++ cms/src/builder/kari.rs | 35 +++++++++++++++++++++++++++++++++++ cms/src/builder/utils.rs | 0 3 files changed, 39 insertions(+) create mode 100644 cms/src/builder/kari.rs create mode 100644 cms/src/builder/utils.rs diff --git a/cms/src/builder.rs b/cms/src/builder.rs index 9f658ef50..bc985c72d 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -47,6 +47,10 @@ use x509_cert::attr::{Attribute, AttributeValue, Attributes}; use x509_cert::builder::{self, AsyncBuilder, Builder}; use zeroize::Zeroize; +// Modules +mod kari; +mod utils; + /// Error type #[derive(Debug)] #[non_exhaustive] diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs new file mode 100644 index 000000000..82524c6c5 --- /dev/null +++ b/cms/src/builder/kari.rs @@ -0,0 +1,35 @@ +//! Key Agreement Recipient Info (Kari) Builder +//! +//! This module contains the building logic for Key Agreement Recipient Info. +//! It partially implements [RFC 5753]. +//! +//! [RFC 5753]: https://datatracker.ietf.org/doc/html/rfc5753 +//! +//! + +/// The `EccCmsSharedInfo` type is defined in [RFC 5753 Section 7.2]. +/// +/// ```text +/// EccCmsSharedInfo ::= SEQUENCE { +/// keyInfo AlgorithmIdentifier, +/// entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL, +/// suppPubInfo [2] EXPLICIT OCTET STRING } +/// ``` +/// +/// [RFC 5753 Section 7.2]: https://www.rfc-editor.org/rfc/rfc5753#section-7.2 +#[derive(Clone, Debug, Eq, PartialEq, Sequence)] +pub struct EccCmsSharedInfo { + /// Object identifier of the key-encryption algorithm + pub key_info: AlgorithmIdentifierOwned, + /// Additional keying material - optional + #[asn1( + context_specific = "0", + tag_mode = "EXPLICIT", + constructed = "true", + optional = "true" + )] + pub entity_u_info: Option, + /// Length of the generated KEK, in bits, represented as a 32-bit number + #[asn1(context_specific = "2", tag_mode = "EXPLICIT", constructed = "true")] + pub supp_pub_info: OctetString, +} diff --git a/cms/src/builder/utils.rs b/cms/src/builder/utils.rs new file mode 100644 index 000000000..e69de29bb From c2a837bb090c92eaf512b739d0b9dde210860d2e Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Fri, 11 Oct 2024 18:39:01 -0400 Subject: [PATCH 02/24] cms: ecc-kari support - add EccCmsSharedInfo --- cms/src/builder/kari.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index 82524c6c5..85f5a6fa6 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -7,6 +7,11 @@ //! //! +use super::AlgorithmIdentifierOwned; +use super::UserKeyingMaterial; + +use der::{asn1::OctetString, Sequence}; + /// The `EccCmsSharedInfo` type is defined in [RFC 5753 Section 7.2]. /// /// ```text From d9d4a24bde391c5d3a112aa458c68488ad45b14f Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sat, 12 Oct 2024 10:20:02 -0400 Subject: [PATCH 03/24] cms: ecc-kari support - add KeyAgreementAlgorithm --- cms/src/builder/kari.rs | 45 +++++++++++++++++++++++++++++++++++++++-- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index 85f5a6fa6..b15a6eb2e 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -7,9 +7,9 @@ //! //! -use super::AlgorithmIdentifierOwned; -use super::UserKeyingMaterial; +use super::{AlgorithmIdentifierOwned, UserKeyingMaterial}; +use const_oid::ObjectIdentifier; use der::{asn1::OctetString, Sequence}; /// The `EccCmsSharedInfo` type is defined in [RFC 5753 Section 7.2]. @@ -38,3 +38,44 @@ pub struct EccCmsSharedInfo { #[asn1(context_specific = "2", tag_mode = "EXPLICIT", constructed = "true")] pub supp_pub_info: OctetString, } + +/// Represents supported key agreement algorithm for ECC - as defined in [RFC 5753 Section 7.1.4]. +/// +/// As per [RFC 5753 Section 8], the following are supported: +/// - dhSinglePass-stdDH-sha224kdf-scheme +/// - dhSinglePass-stdDH-sha256kdf-scheme +/// - dhSinglePass-stdDH-sha384kdf-scheme +/// - dhSinglePass-stdDH-sha512kdf-scheme +/// +/// [RFC 5753 Section 7.1.4]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.1.4 +/// [RFC 5753 Section 8]: https://datatracker.ietf.org/doc/html/rfc5753#section-8 + +pub enum KeyAgreementAlgorithm { + /// dhSinglePass-stdDH-sha224kdf-scheme + SinglePassStdDhSha224Kdf, + /// dhSinglePass-stdDH-sha256kdf-scheme + SinglePassStdDhSha256Kdf, + /// dhSinglePass-stdDH-sha384kdf-scheme + SinglePassStdDhSha384Kdf, + /// dhSinglePass-stdDH-sh512df-scheme + SinglePassStdDhSha512Kdf, +} +impl KeyAgreementAlgorithm { + /// Return the OID of the algorithm. + pub fn oid(&self) -> ObjectIdentifier { + match self { + Self::SinglePassStdDhSha224Kdf => { + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_224_KDF_SCHEME + } + Self::SinglePassStdDhSha256Kdf => { + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_256_KDF_SCHEME + } + Self::SinglePassStdDhSha384Kdf => { + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_384_KDF_SCHEME + } + Self::SinglePassStdDhSha512Kdf => { + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_512_KDF_SCHEME + } + } + } +} From 7e335e12d81698df7fa9e7641472035a4eb9fcab Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sat, 12 Oct 2024 10:36:51 -0400 Subject: [PATCH 04/24] cms: ecc-kari support - add RFC details for KeyAgreementAlgorithm --- cms/src/builder/kari.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index b15a6eb2e..b9599dbfe 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -41,7 +41,24 @@ pub struct EccCmsSharedInfo { /// Represents supported key agreement algorithm for ECC - as defined in [RFC 5753 Section 7.1.4]. /// -/// As per [RFC 5753 Section 8], the following are supported: +/// As per [RFC 5753 Section 8]: +/// ```text +/// Implementations that support EnvelopedData with the ephemeral-static +/// ECDH standard primitive: +/// +/// - MUST support the dhSinglePass-stdDH-sha256kdf-scheme key +/// agreement algorithm, the id-aes128-wrap key wrap algorithm, and +/// the id-aes128-cbc content encryption algorithm; and +/// - MAY support the dhSinglePass-stdDH-sha1kdf-scheme, dhSinglePass- +/// stdDH-sha224kdf-scheme, dhSinglePass-stdDH-sha384kdf-scheme, and +/// dhSinglePass-stdDH-sha512kdf-scheme key agreement algorithms; +/// the id-alg-CMS3DESwrap, id-aes192-wrap, and id-aes256-wrap key +/// wrap algorithms; and the des-ede3-cbc, id-aes192-cbc, and id- +/// aes256-cbc content encryption algorithms; other algorithms MAY +/// also be supported. +/// ``` +/// +/// As such the following are currently supported: /// - dhSinglePass-stdDH-sha224kdf-scheme /// - dhSinglePass-stdDH-sha256kdf-scheme /// - dhSinglePass-stdDH-sha384kdf-scheme @@ -49,7 +66,6 @@ pub struct EccCmsSharedInfo { /// /// [RFC 5753 Section 7.1.4]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.1.4 /// [RFC 5753 Section 8]: https://datatracker.ietf.org/doc/html/rfc5753#section-8 - pub enum KeyAgreementAlgorithm { /// dhSinglePass-stdDH-sha224kdf-scheme SinglePassStdDhSha224Kdf, From 4d2034a59a37199696dcac4b7530c69ae627c3d1 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sat, 12 Oct 2024 14:41:21 -0400 Subject: [PATCH 05/24] cms: ecc-kari support - add KeyWrapAlgorithm --- cms/src/builder/kari.rs | 117 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index b9599dbfe..09a18d2eb 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -10,7 +10,7 @@ use super::{AlgorithmIdentifierOwned, UserKeyingMaterial}; use const_oid::ObjectIdentifier; -use der::{asn1::OctetString, Sequence}; +use der::{asn1::OctetString, Any, Sequence}; /// The `EccCmsSharedInfo` type is defined in [RFC 5753 Section 7.2]. /// @@ -95,3 +95,118 @@ impl KeyAgreementAlgorithm { } } } + +/// Represents supported key wrap algorithm for ECC - as defined in [RFC 5753 Section 7.1.5]. +/// +/// As per [RFC 5753 Section 8]: +/// ```text +/// Implementations that support EnvelopedData with the ephemeral-static +/// ECDH standard primitive: +/// +/// - MUST support the dhSinglePass-stdDH-sha256kdf-scheme key +/// agreement algorithm, the id-aes128-wrap key wrap algorithm, and +/// the id-aes128-cbc content encryption algorithm; and +/// - MAY support the dhSinglePass-stdDH-sha1kdf-scheme, dhSinglePass- +/// stdDH-sha224kdf-scheme, dhSinglePass-stdDH-sha384kdf-scheme, and +/// dhSinglePass-stdDH-sha512kdf-scheme key agreement algorithms; +/// the id-alg-CMS3DESwrap, id-aes192-wrap, and id-aes256-wrap key +/// wrap algorithms; and the des-ede3-cbc, id-aes192-cbc, and id- +/// aes256-cbc content encryption algorithms; other algorithms MAY +/// also be supported. +/// ``` +/// +/// As such the following algorithm are currently supported +/// - id-aes128-wrap +/// - id-aes192-wrap - (OPTIONAL) +/// - id-aes256-wrap - (OPTIONAL) +/// +/// [RFC 5753 Section 8]: https://datatracker.ietf.org/doc/html/rfc5753#section-8 +/// [RFC 5753 Section 7.1.5]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.1.5 +#[derive(Copy, Clone)] +pub enum KeyWrapAlgorithm { + /// id-aes128-wrap + Aes128, + /// id-aes192-wrap + Aes192, + /// id-aes256-wrap + Aes256, +} +impl KeyWrapAlgorithm { + /// Return the Object Identifier (OID) of the algorithm. + /// + /// OID are defined in [RFC 3565 Section 2.3.2] + /// + /// [RFC 3565 Section 2.3.2]: + /// ```text + /// NIST has assigned the following OIDs to define the AES key wrap + /// algorithm. + /// + /// id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } + /// id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } + /// id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } + /// + /// In all cases the parameters field MUST be absent. + /// ``` + /// + /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 + fn oid(&self) -> ObjectIdentifier { + match self { + Self::Aes128 => const_oid::db::rfc5911::ID_AES_128_WRAP, + Self::Aes192 => const_oid::db::rfc5911::ID_AES_192_WRAP, + Self::Aes256 => const_oid::db::rfc5911::ID_AES_256_WRAP, + } + } + + /// Return parameters of the algorithm to be used in the context of `AlgorithmIdentifierOwned`. + /// + /// It should be absent as defined in [RFC 3565 Section 2.3.2] and per usage in [RFC 5753 Section 7.2]. + /// + /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 + /// [RFC 5753 Section 7.2]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.2 + fn parameters(&self) -> Option { + match self { + Self::Aes128 => None, + Self::Aes192 => None, + Self::Aes256 => None, + } + } + + /// Return key size of the algorithm in number of bits + pub fn key_size_in_bits(&self) -> u32 { + match self { + Self::Aes128 => 128, + Self::Aes192 => 192, + Self::Aes256 => 256, + } + } +} +impl From for AlgorithmIdentifierOwned { + /// Convert a `KeyWrapAlgorithm` to the corresponding `AlgorithmIdentifierOwned`. + /// + /// Conversion is done as defined in [RFC 3565 Section 2.3.2] and according to [RFC 5753 Section 7.2]: + /// + /// [RFC 3565 Section 2.3.2] + /// ```text + /// keyInfo contains the object identifier of the key-encryption + /// algorithm (used to wrap the CEK) and associated parameters. In + /// this specification, 3DES wrap has NULL parameters while the AES + /// wraps have absent parameters. + /// ``` + /// + /// [RFC 5753 Section 73.2] + /// ```text + /// keyInfo contains the object identifier of the key-encryption + /// algorithm (used to wrap the CEK) and associated parameters. In + /// this specification, 3DES wrap has NULL parameters while the AES + /// wraps have absent parameters. + /// ``` + /// + /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 + /// [RFC 5753 Section 7.2]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.2 + fn from(kw_algo: KeyWrapAlgorithm) -> Self { + Self { + oid: kw_algo.oid(), + parameters: kw_algo.parameters(), + } + } +} From 78ecc1a07478a7907454ef16834d3989eba7cf19 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sat, 12 Oct 2024 14:42:34 -0400 Subject: [PATCH 06/24] cms: ecc-kari support - add elliptic-curve dependency --- cms/Cargo.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index d6b604e26..1a431a166 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -25,6 +25,7 @@ aes = { version = "=0.9.0-pre.2", optional = true } async-signature = { version = "=0.6.0-pre.4", features = ["digest", "rand_core"], optional = true } cbc = { version = "=0.2.0-pre.2", optional = true } cipher = { version = "=0.5.0-pre.7", features = ["alloc", "block-padding", "rand_core"], optional = true } +elliptic-curve = { version = "=0.14.0-rc.1", optional = true } rsa = { version = "=0.10.0-pre.3", optional = true } sha1 = { version = "=0.11.0-pre.4", optional = true } sha2 = { version = "=0.11.0-pre.4", optional = true } @@ -45,12 +46,14 @@ tokio = { version = "1.40.0", features = ["macros", "rt"] } x509-cert = { version = "=0.3.0-pre.0", features = ["pem"] } [features] +default = ["builder"] std = ["der/std", "spki/std"] builder = [ "dep:aes", "dep:async-signature", "dep:cbc", "dep:cipher", + "elliptic-curve/ecdh", "dep:rsa", "dep:sha1", "dep:sha2", From 03e599a9594e105f822434c2cbc9dc3fbf686142 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 13 Oct 2024 09:27:41 -0400 Subject: [PATCH 07/24] cms: ecc-kari support - add aes-kw dependency --- cms/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 1a431a166..4c53015a1 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -22,6 +22,7 @@ x509-cert = { version = "=0.3.0-pre.0", default-features = false } # optional dependencies aes = { version = "=0.9.0-pre.2", optional = true } +aes-kw = { version ="0.2.1", optional = true } async-signature = { version = "=0.6.0-pre.4", features = ["digest", "rand_core"], optional = true } cbc = { version = "=0.2.0-pre.2", optional = true } cipher = { version = "=0.5.0-pre.7", features = ["alloc", "block-padding", "rand_core"], optional = true } @@ -50,6 +51,7 @@ default = ["builder"] std = ["der/std", "spki/std"] builder = [ "dep:aes", + "dep:aes-kw", "dep:async-signature", "dep:cbc", "dep:cipher", From 914ed5936bc95fa06a2e09284ef82b3a8fc526b1 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:07:46 -0400 Subject: [PATCH 08/24] cms: ecc-kari support - move KeyWrapAlgorithm to kw module --- cms/src/builder/kari.rs | 145 ++++++---------------------------------- 1 file changed, 19 insertions(+), 126 deletions(-) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index 09a18d2eb..2639a4d61 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -5,12 +5,10 @@ //! //! [RFC 5753]: https://datatracker.ietf.org/doc/html/rfc5753 //! -//! - -use super::{AlgorithmIdentifierOwned, UserKeyingMaterial}; +use super::{utils::HashDigest, AlgorithmIdentifierOwned, UserKeyingMaterial}; use const_oid::ObjectIdentifier; -use der::{asn1::OctetString, Any, Sequence}; +use der::{asn1::OctetString, Sequence}; /// The `EccCmsSharedInfo` type is defined in [RFC 5753 Section 7.2]. /// @@ -43,19 +41,19 @@ pub struct EccCmsSharedInfo { /// /// As per [RFC 5753 Section 8]: /// ```text -/// Implementations that support EnvelopedData with the ephemeral-static -/// ECDH standard primitive: +/// Implementations that support EnvelopedData with the ephemeral-static +/// ECDH standard primitive: /// -/// - MUST support the dhSinglePass-stdDH-sha256kdf-scheme key +/// - MUST support the dhSinglePass-stdDH-sha256kdf-scheme key /// agreement algorithm, the id-aes128-wrap key wrap algorithm, and /// the id-aes128-cbc content encryption algorithm; and /// - MAY support the dhSinglePass-stdDH-sha1kdf-scheme, dhSinglePass- -/// stdDH-sha224kdf-scheme, dhSinglePass-stdDH-sha384kdf-scheme, and -/// dhSinglePass-stdDH-sha512kdf-scheme key agreement algorithms; -/// the id-alg-CMS3DESwrap, id-aes192-wrap, and id-aes256-wrap key -/// wrap algorithms; and the des-ede3-cbc, id-aes192-cbc, and id- -/// aes256-cbc content encryption algorithms; other algorithms MAY -/// also be supported. +/// stdDH-sha224kdf-scheme, dhSinglePass-stdDH-sha384kdf-scheme, and +/// dhSinglePass-stdDH-sha512kdf-scheme key agreement algorithms; +/// the id-alg-CMS3DESwrap, id-aes192-wrap, and id-aes256-wrap key +/// wrap algorithms; and the des-ede3-cbc, id-aes192-cbc, and id- +/// aes256-cbc content encryption algorithms; other algorithms MAY +/// also be supported. /// ``` /// /// As such the following are currently supported: @@ -78,7 +76,7 @@ pub enum KeyAgreementAlgorithm { } impl KeyAgreementAlgorithm { /// Return the OID of the algorithm. - pub fn oid(&self) -> ObjectIdentifier { + fn oid(&self) -> ObjectIdentifier { match self { Self::SinglePassStdDhSha224Kdf => { const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_224_KDF_SCHEME @@ -95,118 +93,13 @@ impl KeyAgreementAlgorithm { } } } - -/// Represents supported key wrap algorithm for ECC - as defined in [RFC 5753 Section 7.1.5]. -/// -/// As per [RFC 5753 Section 8]: -/// ```text -/// Implementations that support EnvelopedData with the ephemeral-static -/// ECDH standard primitive: -/// -/// - MUST support the dhSinglePass-stdDH-sha256kdf-scheme key -/// agreement algorithm, the id-aes128-wrap key wrap algorithm, and -/// the id-aes128-cbc content encryption algorithm; and -/// - MAY support the dhSinglePass-stdDH-sha1kdf-scheme, dhSinglePass- -/// stdDH-sha224kdf-scheme, dhSinglePass-stdDH-sha384kdf-scheme, and -/// dhSinglePass-stdDH-sha512kdf-scheme key agreement algorithms; -/// the id-alg-CMS3DESwrap, id-aes192-wrap, and id-aes256-wrap key -/// wrap algorithms; and the des-ede3-cbc, id-aes192-cbc, and id- -/// aes256-cbc content encryption algorithms; other algorithms MAY -/// also be supported. -/// ``` -/// -/// As such the following algorithm are currently supported -/// - id-aes128-wrap -/// - id-aes192-wrap - (OPTIONAL) -/// - id-aes256-wrap - (OPTIONAL) -/// -/// [RFC 5753 Section 8]: https://datatracker.ietf.org/doc/html/rfc5753#section-8 -/// [RFC 5753 Section 7.1.5]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.1.5 -#[derive(Copy, Clone)] -pub enum KeyWrapAlgorithm { - /// id-aes128-wrap - Aes128, - /// id-aes192-wrap - Aes192, - /// id-aes256-wrap - Aes256, -} -impl KeyWrapAlgorithm { - /// Return the Object Identifier (OID) of the algorithm. - /// - /// OID are defined in [RFC 3565 Section 2.3.2] - /// - /// [RFC 3565 Section 2.3.2]: - /// ```text - /// NIST has assigned the following OIDs to define the AES key wrap - /// algorithm. - /// - /// id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } - /// id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } - /// id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } - /// - /// In all cases the parameters field MUST be absent. - /// ``` - /// - /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 - fn oid(&self) -> ObjectIdentifier { - match self { - Self::Aes128 => const_oid::db::rfc5911::ID_AES_128_WRAP, - Self::Aes192 => const_oid::db::rfc5911::ID_AES_192_WRAP, - Self::Aes256 => const_oid::db::rfc5911::ID_AES_256_WRAP, - } - } - - /// Return parameters of the algorithm to be used in the context of `AlgorithmIdentifierOwned`. - /// - /// It should be absent as defined in [RFC 3565 Section 2.3.2] and per usage in [RFC 5753 Section 7.2]. - /// - /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 - /// [RFC 5753 Section 7.2]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.2 - fn parameters(&self) -> Option { - match self { - Self::Aes128 => None, - Self::Aes192 => None, - Self::Aes256 => None, - } - } - - /// Return key size of the algorithm in number of bits - pub fn key_size_in_bits(&self) -> u32 { - match self { - Self::Aes128 => 128, - Self::Aes192 => 192, - Self::Aes256 => 256, - } - } -} -impl From for AlgorithmIdentifierOwned { - /// Convert a `KeyWrapAlgorithm` to the corresponding `AlgorithmIdentifierOwned`. - /// - /// Conversion is done as defined in [RFC 3565 Section 2.3.2] and according to [RFC 5753 Section 7.2]: - /// - /// [RFC 3565 Section 2.3.2] - /// ```text - /// keyInfo contains the object identifier of the key-encryption - /// algorithm (used to wrap the CEK) and associated parameters. In - /// this specification, 3DES wrap has NULL parameters while the AES - /// wraps have absent parameters. - /// ``` - /// - /// [RFC 5753 Section 73.2] - /// ```text - /// keyInfo contains the object identifier of the key-encryption - /// algorithm (used to wrap the CEK) and associated parameters. In - /// this specification, 3DES wrap has NULL parameters while the AES - /// wraps have absent parameters. - /// ``` - /// - /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 - /// [RFC 5753 Section 7.2]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.2 - fn from(kw_algo: KeyWrapAlgorithm) -> Self { - Self { - oid: kw_algo.oid(), - parameters: kw_algo.parameters(), +impl From<&KeyAgreementAlgorithm> for HashDigest { + fn from(ka_algo: &KeyAgreementAlgorithm) -> Self { + match ka_algo { + KeyAgreementAlgorithm::SinglePassStdDhSha224Kdf => Self::Sha224, + KeyAgreementAlgorithm::SinglePassStdDhSha256Kdf => Self::Sha256, + KeyAgreementAlgorithm::SinglePassStdDhSha384Kdf => Self::Sha384, + KeyAgreementAlgorithm::SinglePassStdDhSha512Kdf => Self::Sha512, } } } From 0fbe363feb7603e3e6021f6e60f4a0ae6aaafc0c Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 13 Oct 2024 16:28:47 -0400 Subject: [PATCH 09/24] cms: ecc-kari support - add EcKeyEncryptionInfo --- cms/src/builder/kari.rs | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index 2639a4d61..157786670 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -8,7 +8,8 @@ use super::{utils::HashDigest, AlgorithmIdentifierOwned, UserKeyingMaterial}; use const_oid::ObjectIdentifier; -use der::{asn1::OctetString, Sequence}; +use der::{asn1::OctetString, Any, Sequence}; +use elliptic_curve::{CurveArithmetic, PublicKey}; /// The `EccCmsSharedInfo` type is defined in [RFC 5753 Section 7.2]. /// @@ -103,3 +104,34 @@ impl From<&KeyAgreementAlgorithm> for HashDigest { } } } + +/// Contains information required to encrypt the content encryption key with a method based on ECC key agreement +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum EcKeyEncryptionInfo +where + C: CurveArithmetic, +{ + /// Encrypt key with EC + Ec(PublicKey), +} +impl EcKeyEncryptionInfo +where + C: CurveArithmetic + const_oid::AssociatedOid, +{ + /// Returns the OID associated with the curve used in this `EcKeyEncryptionInfo`. + pub fn get_oid(&self) -> ObjectIdentifier { + C::OID + } +} +impl From<&EcKeyEncryptionInfo> for AlgorithmIdentifierOwned +where + C: CurveArithmetic + const_oid::AssociatedOid, +{ + fn from(ec_key_encryption_info: &EcKeyEncryptionInfo) -> Self { + let parameters = Some(Any::from(&ec_key_encryption_info.get_oid())); + AlgorithmIdentifierOwned { + oid: elliptic_curve::ALGORITHM_OID, // id-ecPublicKey + parameters, // Curve OID + } + } +} From dd3b09d77a7b17822761ad2d29bb95af03cd8e8e Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Mon, 14 Oct 2024 19:47:20 -0400 Subject: [PATCH 10/24] cms: ecc-kari support - add kdf dependency --- cms/Cargo.toml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 4c53015a1..6ec8fe617 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -23,6 +23,7 @@ x509-cert = { version = "=0.3.0-pre.0", default-features = false } # optional dependencies aes = { version = "=0.9.0-pre.2", optional = true } aes-kw = { version ="0.2.1", optional = true } +ansi-x963-kdf = { git = "https://github.com/RustCrypto/KDFs.git", version = "0.1.0", optional = true } async-signature = { version = "=0.6.0-pre.4", features = ["digest", "rand_core"], optional = true } cbc = { version = "=0.2.0-pre.2", optional = true } cipher = { version = "=0.5.0-pre.7", features = ["alloc", "block-padding", "rand_core"], optional = true } @@ -52,6 +53,7 @@ std = ["der/std", "spki/std"] builder = [ "dep:aes", "dep:aes-kw", + "dep:ansi-x963-kdf", "dep:async-signature", "dep:cbc", "dep:cipher", From 92cc97060b0262d4570f3e0f7b2e67920e603aaa Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:36:50 -0400 Subject: [PATCH 11/24] cms: ecc-kari support - move KeyAgreeRecipientInfoBuilder to sub-module --- Cargo.lock | 118 +++++++++++++++++++++++++++++++++++++++------ cms/src/builder.rs | 54 ++------------------- 2 files changed, 107 insertions(+), 65 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3831efad..b36dc3af9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,7 +23,18 @@ version = "0.6.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5f451b77e2f92932dc411da6ef9f3d33efad68a6f14a7a83e559453458e85ac" dependencies = [ - "crypto-common", + "crypto-common 0.2.0-rc.1", +] + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher 0.4.4", + "cpufeatures", ] [[package]] @@ -33,7 +44,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7856582c758ade85d71daf27ec6bcea6c1c73913692b07b8dffea2dc03531c9" dependencies = [ "cfg-if", - "cipher", + "cipher 0.5.0-pre.7", "cpufeatures", ] @@ -44,13 +55,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cce27af05d45b901bb28da33ff8b2b2b2044f595b24fc0f36d4882dae91d484" dependencies = [ "aead", - "aes", - "cipher", + "aes 0.9.0-pre.2", + "cipher 0.5.0-pre.7", "ctr", "ghash", "subtle", ] +[[package]] +name = "aes-kw" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" +dependencies = [ + "aes 0.8.4", +] + [[package]] name = "aho-corasick" version = "1.1.3" @@ -66,6 +86,14 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" +[[package]] +name = "ansi-x963-kdf" +version = "0.1.0" +source = "git+https://github.com/RustCrypto/KDFs.git#6f0b04d3fc1e6b7b7fa1ebca4d2be52d9e107dc9" +dependencies = [ + "digest", +] + [[package]] name = "anstream" version = "0.3.2" @@ -264,7 +292,7 @@ version = "0.2.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0729a0a8422deb6056b8fcd89c42b724fe27e69458fa006f00c63cbffffd91b" dependencies = [ - "cipher", + "cipher 0.5.0-pre.7", ] [[package]] @@ -300,14 +328,24 @@ dependencies = [ "half", ] +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common 0.1.6", + "inout 0.1.3", +] + [[package]] name = "cipher" version = "0.5.0-pre.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b1425e6ce000f05a73096556cabcfb6a10a3ffe3bb4d75416ca8f00819c0b6a" dependencies = [ - "crypto-common", - "inout", + "crypto-common 0.2.0-rc.1", + "inout 0.2.0-rc.1", ] [[package]] @@ -353,13 +391,16 @@ dependencies = [ name = "cms" version = "0.3.0-pre.0" dependencies = [ - "aes", + "aes 0.9.0-pre.2", + "aes-kw", + "ansi-x963-kdf", "async-signature", "cbc", - "cipher", + "cipher 0.5.0-pre.7", "const-oid", "der", "ecdsa", + "elliptic-curve", "getrandom", "hex-literal", "p256", @@ -464,6 +505,16 @@ dependencies = [ "zeroize", ] +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + [[package]] name = "crypto-common" version = "0.2.0-rc.1" @@ -481,7 +532,7 @@ version = "0.10.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e1482d284b80d7fddb211666d513dc5e23b0cc3a03ad398ff70543827c789f" dependencies = [ - "cipher", + "cipher 0.5.0-pre.7", ] [[package]] @@ -535,7 +586,7 @@ version = "0.9.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76239c731adb4b5204cfeec47bd06ec1071d9477a0d32bbb83dc7d8c599efe63" dependencies = [ - "cipher", + "cipher 0.5.0-pre.7", ] [[package]] @@ -546,7 +597,7 @@ checksum = "cf2e3d6615d99707295a9673e889bf363a04b2a466bd320c65a72536f7577379" dependencies = [ "block-buffer", "const-oid", - "crypto-common", + "crypto-common 0.2.0-rc.1", "subtle", ] @@ -581,6 +632,7 @@ dependencies = [ "digest", "ff", "group", + "hkdf", "hybrid-array", "pem-rfc7468", "pkcs8", @@ -729,6 +781,16 @@ dependencies = [ "slab", ] +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + [[package]] name = "getrandom" version = "0.2.15" @@ -811,6 +873,15 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" +[[package]] +name = "hkdf" +version = "0.13.0-pre.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00176ff81091018d42ff82e8324f8e5adb0b7e0468d1358f653972562dbff031" +dependencies = [ + "hmac", +] + [[package]] name = "hmac" version = "0.13.0-pre.4" @@ -840,6 +911,15 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + [[package]] name = "inout" version = "0.2.0-rc.1" @@ -1088,7 +1168,7 @@ dependencies = [ name = "pkcs5" version = "0.8.0-rc.2" dependencies = [ - "aes", + "aes 0.9.0-pre.2", "aes-gcm", "cbc", "der", @@ -1414,7 +1494,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1affa54a576c40080654b494bb3f3198fa2fe46e0954b85196d122e3561c2fd0" dependencies = [ "cfg-if", - "cipher", + "cipher 0.5.0-pre.7", ] [[package]] @@ -1813,7 +1893,7 @@ version = "0.6.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3517d72c5ca6d60f9f2e85d2c772e2652830062a685105a528d19dd823cf87d5" dependencies = [ - "crypto-common", + "crypto-common 0.2.0-rc.1", "subtle", ] @@ -1823,6 +1903,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + [[package]] name = "wait-timeout" version = "0.2.0" @@ -1863,7 +1949,7 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] diff --git a/cms/src/builder.rs b/cms/src/builder.rs index bc985c72d..c269c5c59 100644 --- a/cms/src/builder.rs +++ b/cms/src/builder.rs @@ -6,8 +6,7 @@ use crate::cert::CertificateChoices; use crate::content_info::{CmsVersion, ContentInfo}; use crate::enveloped_data::{ EncryptedContentInfo, EncryptedKey, EnvelopedData, KekIdentifier, KeyTransRecipientInfo, - OriginatorIdentifierOrKey, OriginatorInfo, RecipientIdentifier, RecipientInfo, RecipientInfos, - UserKeyingMaterial, + OriginatorInfo, RecipientIdentifier, RecipientInfo, RecipientInfos, UserKeyingMaterial, }; use crate::revocation::{RevocationInfoChoice, RevocationInfoChoices}; use crate::signed_data::{ @@ -51,6 +50,10 @@ use zeroize::Zeroize; mod kari; mod utils; +// Exports +pub use kari::{EcKeyEncryptionInfo, KeyAgreeRecipientInfoBuilder, KeyAgreementAlgorithm}; +pub use utils::kw::KeyWrapAlgorithm; + /// Error type #[derive(Debug)] #[non_exhaustive] @@ -697,53 +700,6 @@ where } } -/// Builds a `KeyAgreeRecipientInfo` according to RFC 5652 § 6. -/// This type uses key agreement: the recipient's public key and the sender's -/// private key are used to generate a pairwise symmetric key, then -/// the content-encryption key is encrypted in the pairwise symmetric key. -pub struct KeyAgreeRecipientInfoBuilder { - /// A CHOICE with three alternatives specifying the sender's key agreement public key. - pub originator: OriginatorIdentifierOrKey, - /// Optional information which helps generating different keys every time. - pub ukm: Option, - /// Encryption algorithm to be used for key encryption - pub key_enc_alg: AlgorithmIdentifierOwned, -} - -impl KeyAgreeRecipientInfoBuilder { - /// Creates a `KeyAgreeRecipientInfoBuilder` - pub fn new( - originator: OriginatorIdentifierOrKey, - ukm: Option, - key_enc_alg: AlgorithmIdentifierOwned, - ) -> Result { - Ok(KeyAgreeRecipientInfoBuilder { - originator, - ukm, - key_enc_alg, - }) - } -} - -impl RecipientInfoBuilder for KeyAgreeRecipientInfoBuilder { - /// Returns the RecipientInfoType - fn recipient_info_type(&self) -> RecipientInfoType { - RecipientInfoType::Kari - } - - /// Returns the `CMSVersion` for this `RecipientInfo` - fn recipient_info_version(&self) -> CmsVersion { - CmsVersion::V3 - } - - /// Build a `KeyAgreeRecipientInfoBuilder`. See RFC 5652 § 6.2.1 - fn build(&mut self, _content_encryption_key: &[u8]) -> Result { - Err(Error::Builder(String::from( - "Building KeyAgreeRecipientInfo is not implemented, yet.", - ))) - } -} - /// Builds a `KekRecipientInfo` according to RFC 5652 § 6. /// Uses symmetric key-encryption keys: the content-encryption key is /// encrypted in a previously distributed symmetric key-encryption key. From 5a053eae99ac7491974a93f11c4a7eb98cebc26d Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sat, 19 Oct 2024 19:37:28 -0400 Subject: [PATCH 12/24] cms: ecc-kari support - add elliptic-curve/pkcs8 --- cms/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 6ec8fe617..5371e1485 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -58,6 +58,7 @@ builder = [ "dep:cbc", "dep:cipher", "elliptic-curve/ecdh", + "elliptic-curve/pkcs8", "dep:rsa", "dep:sha1", "dep:sha2", From 139828e935155e0af2f990f8f01f891a01a27e77 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:09:21 -0400 Subject: [PATCH 13/24] cms: ecc-kari support - add KDF utilities --- cms/src/builder/utils/kdf.rs | 175 +++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) create mode 100644 cms/src/builder/utils/kdf.rs diff --git a/cms/src/builder/utils/kdf.rs b/cms/src/builder/utils/kdf.rs new file mode 100644 index 000000000..a99b2399c --- /dev/null +++ b/cms/src/builder/utils/kdf.rs @@ -0,0 +1,175 @@ +//! KDF module +//! +//! This module contains KDF logic. +//! + +use crate::builder::{Error, Result}; +use alloc::string::String; + +#[derive(PartialEq, Eq, Debug)] +pub(in crate::builder) enum HashDigest { + Sha224, + Sha256, + Sha384, + Sha512, +} + +/// Wrapper for ANSI-X9.63 KDF +/// +/// This function wraps ansi_x963_kdf, applying a Hash Disgest based on the key agreement algorithm identifier +pub(in crate::builder) fn try_ansi_x963_kdf( + secret: &[u8], + other_info: &[u8], + key: &mut impl AsMut<[u8]>, + digest: &HashDigest, +) -> Result<()> { + match digest { + HashDigest::Sha224 => ansi_x963_kdf_sha224(secret, other_info, key).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf SHA-224", + )) + }), + HashDigest::Sha256 => ansi_x963_kdf_sha256(secret, other_info, key).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf SHA-256", + )) + }), + HashDigest::Sha384 => ansi_x963_kdf_sha384(secret, other_info, key).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf SHA-384", + )) + }), + HashDigest::Sha512 => ansi_x963_kdf_sha512(secret, other_info, key).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf SHA-512", + )) + }), + } +} + +/// ANSI-X9.63-KDF with SHA224 +fn ansi_x963_kdf_sha224( + secret: &[u8], + other_info: &[u8], + key: &mut impl AsMut<[u8]>, +) -> Result<()> { + ansi_x963_kdf::derive_key_into::(secret, other_info, key.as_mut()).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf", + )) + }) +} + +/// ANSI-X9.63-KDF with SHA256 +fn ansi_x963_kdf_sha256( + secret: &[u8], + other_info: &[u8], + key: &mut impl AsMut<[u8]>, +) -> Result<()> { + ansi_x963_kdf::derive_key_into::(secret, other_info, key.as_mut()).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf", + )) + }) +} + +/// ANSI-X9.63-KDF with SHA384 +fn ansi_x963_kdf_sha384( + secret: &[u8], + other_info: &[u8], + key: &mut impl AsMut<[u8]>, +) -> Result<()> { + ansi_x963_kdf::derive_key_into::(secret, other_info, key.as_mut()).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf", + )) + }) +} + +/// ANSI-X9.63-KDF with SHA512 +fn ansi_x963_kdf_sha512( + secret: &[u8], + other_info: &[u8], + key: &mut impl AsMut<[u8]>, +) -> Result<()> { + ansi_x963_kdf::derive_key_into::(secret, other_info, key.as_mut()).map_err(|_| { + Error::Builder(String::from( + "Could not generate a shared secret via ansi-x9.63-kdf", + )) + }) +} + +#[cfg(test)] +mod tests { + + use super::*; + + #[test] + fn test_try_ansi_x963_kdf() { + let secret = [0u8; 16]; + let other_info = []; + let mut key = [0u8; 16]; + assert!(try_ansi_x963_kdf(&secret, &other_info, &mut key, &HashDigest::Sha224).is_ok()); + assert_eq!( + key, + [33, 35, 2, 122, 169, 122, 164, 137, 12, 5, 195, 31, 101, 142, 44, 237] + ) + } + + #[test] + fn test_try_ansi_x963_kdf_error() { + // Empty secret should trigger error of ansi_x963_kdf + let secret = []; + let other_info = []; + let mut key = [0u8; 16]; + assert!(try_ansi_x963_kdf(&secret, &other_info, &mut key, &HashDigest::Sha224).is_err()); + } + + #[test] + fn test_try_ansi_x963_kdf_sha224() { + let secret = [0u8; 16]; + let other_info = []; + let mut key = [0u8; 16]; + assert!(ansi_x963_kdf_sha224(&secret, &other_info, &mut key).is_ok()); + assert_eq!( + key, + [33, 35, 2, 122, 169, 122, 164, 137, 12, 5, 195, 31, 101, 142, 44, 237] + ) + } + + #[test] + fn test_try_ansi_x963_kdf_sha256() { + let secret = [0u8; 16]; + let other_info = []; + let mut key = [0u8; 16]; + assert!(ansi_x963_kdf_sha256(&secret, &other_info, &mut key).is_ok()); + assert_eq!( + key, + [233, 255, 14, 110, 109, 233, 93, 165, 111, 240, 159, 78, 62, 15, 72, 29] + ) + } + + #[test] + fn test_try_ansi_x963_kdf_sha384() { + let secret = [0u8; 16]; + let other_info = []; + let mut key = [0u8; 16]; + assert!(ansi_x963_kdf_sha384(&secret, &other_info, &mut key).is_ok()); + assert_eq!( + key, + [156, 231, 52, 7, 234, 137, 225, 91, 29, 49, 193, 212, 25, 40, 137, 8] + ) + } + + #[test] + fn test_try_ansi_x963_kdf_sha512() { + let secret = [0u8; 16]; + let other_info = []; + let mut key = [0u8; 16]; + assert!(ansi_x963_kdf_sha512(&secret, &other_info, &mut key).is_ok()); + assert_eq!( + key, + [160, 237, 224, 79, 173, 198, 48, 115, 203, 162, 233, 108, 204, 185, 88, 209] + ) + } +} From 885d93463727621f48bfb5afa0830248c8062558 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:09:42 -0400 Subject: [PATCH 14/24] cms: ecc-kari support - add key wrap utilities --- cms/src/builder/utils/kw.rs | 531 ++++++++++++++++++++++++++++++++++++ 1 file changed, 531 insertions(+) create mode 100644 cms/src/builder/utils/kw.rs diff --git a/cms/src/builder/utils/kw.rs b/cms/src/builder/utils/kw.rs new file mode 100644 index 000000000..db7b9e825 --- /dev/null +++ b/cms/src/builder/utils/kw.rs @@ -0,0 +1,531 @@ +//! Key wrap module +//! +//! This module contains the key wrapping logic based on aes-kw algorithms +//! + +use alloc::{string::String, vec::Vec}; + +use crate::builder::{Error, Result}; +use aes_kw::Kek; +use const_oid::ObjectIdentifier; +use der::Any; +use spki::AlgorithmIdentifierOwned; + +/// Represents supported key wrap algorithm for ECC - as defined in [RFC 5753 Section 7.1.5]. +/// +/// As per [RFC 5753 Section 8]: +/// ```text +/// Implementations that support EnvelopedData with the ephemeral-static +/// ECDH standard primitive: +/// +/// - MUST support the dhSinglePass-stdDH-sha256kdf-scheme key +/// agreement algorithm, the id-aes128-wrap key wrap algorithm, and +/// the id-aes128-cbc content encryption algorithm; and +/// - MAY support the dhSinglePass-stdDH-sha1kdf-scheme, dhSinglePass- +/// stdDH-sha224kdf-scheme, dhSinglePass-stdDH-sha384kdf-scheme, and +/// dhSinglePass-stdDH-sha512kdf-scheme key agreement algorithms; +/// the id-alg-CMS3DESwrap, id-aes192-wrap, and id-aes256-wrap key +/// wrap algorithms; and the des-ede3-cbc, id-aes192-cbc, and id- +/// aes256-cbc content encryption algorithms; other algorithms MAY +/// also be supported. +/// ``` +/// +/// As such the following algorithm are currently supported +/// - id-aes128-wrap +/// - id-aes192-wrap +/// - id-aes256-wrap +/// +/// [RFC 5753 Section 8]: https://datatracker.ietf.org/doc/html/rfc5753#section-8 +/// [RFC 5753 Section 7.1.5]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.1.5 +#[derive(Copy, Clone)] +pub enum KeyWrapAlgorithm { + /// id-aes128-wrap + Aes128, + /// id-aes192-wrap + Aes192, + /// id-aes256-wrap + Aes256, +} +impl KeyWrapAlgorithm { + /// Return the Object Identifier (OID) of the algorithm. + /// + /// OID are defined in [RFC 3565 Section 2.3.2] + /// + /// [RFC 3565 Section 2.3.2]: + /// ```text + /// NIST has assigned the following OIDs to define the AES key wrap + /// algorithm. + /// + /// id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } + /// id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } + /// id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } + /// + /// In all cases the parameters field MUST be absent. + /// ``` + /// + /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 + fn oid(&self) -> ObjectIdentifier { + match self { + Self::Aes128 => const_oid::db::rfc5911::ID_AES_128_WRAP, + Self::Aes192 => const_oid::db::rfc5911::ID_AES_192_WRAP, + Self::Aes256 => const_oid::db::rfc5911::ID_AES_256_WRAP, + } + } + + /// Return parameters of the algorithm to be used in the context of `AlgorithmIdentifierOwned`. + /// + /// It should be absent as defined in [RFC 3565 Section 2.3.2] and per usage in [RFC 5753 Section 7.2]. + /// + /// [RFC 3565 Section 2.3.2]: + /// ```text + /// NIST has assigned the following OIDs to define the AES key wrap + /// algorithm. + /// + /// id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } + /// id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } + /// id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } + /// + /// In all cases the parameters field MUST be absent. + /// ``` + /// + /// [RFC 3565 Section 2.3.2]: https://datatracker.ietf.org/doc/html/rfc3565#section-2.3.2 + /// [RFC 5753 Section 7.2]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.2 + fn parameters(&self) -> Option { + match self { + Self::Aes128 => None, + Self::Aes192 => None, + Self::Aes256 => None, + } + } + + /// Return key size of the algorithm in number of bits + pub fn key_size_in_bits(&self) -> u32 { + match self { + Self::Aes128 => 128, + Self::Aes192 => 192, + Self::Aes256 => 256, + } + } +} +impl From for AlgorithmIdentifierOwned { + /// Convert a `KeyWrapAlgorithm` to the corresponding `AlgorithmIdentifierOwned`. + /// + /// Conversion is done according to [RFC 5753 Section 7.2]: + /// + /// + /// [RFC 5753 Section 7.2] + /// ```text + /// keyInfo contains the object identifier of the key-encryption + /// algorithm (used to wrap the CEK) and associated parameters. In + /// this specification, 3DES wrap has NULL parameters while the AES + /// wraps have absent parameters. + /// ``` + /// + /// [RFC 5753 Section 7.2]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.2 + fn from(kw_algo: KeyWrapAlgorithm) -> Self { + Self { + oid: kw_algo.oid(), + parameters: kw_algo.parameters(), + } + } +} + +/// This struct can be used to perform key wrapping operation. +/// +/// It abstracts some of the key-wrapping logic over incoming wrapping-key and outgoing wrapped-key of different sizes. +/// It currently implements: +/// - try_new() - initialize a key wrapper with right sized depending on KeyWrapAlgorithm and key-to-wrap size +/// - try_wrap() - wrap a key with the corresponding aes-key-wrap algorithms +/// +/// # Note +/// For convenience KeyWrapper can: +/// - yield the inner wrapping-key as a mutable reference (e.g. to use with a KDF) +/// - convert to Vec to obtain Owned data to the wrapped key +#[derive(Debug, Clone, Copy)] +pub(in crate::builder) struct KeyWrapper { + /// Wrapping key + wrapping_key: WrappingKey, + /// Wrapped key + wrapped_key: WrappedKey, +} +impl KeyWrapper { + /// Initialize a new KeyWrapper based on `KeyWrapAlgorithm` and key-to-wrap size. + pub(in crate::builder) fn try_new(kw_algo: &KeyWrapAlgorithm, key_size: usize) -> Result { + let wrapped_key = WrappedKey::try_from(key_size)?; + let wrapping_key = WrappingKey::from(kw_algo); + + Ok(Self { + wrapping_key, + wrapped_key, + }) + } + /// Wraps a given key. + /// + /// This function attempts to wrap the provided `target_key`. + /// + /// # Arguments + /// * `target_key` - A slice of bytes representing the key to be wrapped. + pub(in crate::builder) fn try_wrap(&mut self, target_key: &[u8]) -> Result<()> { + match self.wrapping_key { + WrappingKey::Aes128(wrap_key) => Kek::from(wrap_key) + .wrap(target_key, self.wrapped_key.as_mut()) + .map_err(|_| { + Error::Builder(String::from( + "could not wrap key with Aes128 key wrap algorithm", + )) + }), + WrappingKey::Aes192(kek) => Kek::from(kek) + .wrap(target_key, self.wrapped_key.as_mut()) + .map_err(|_| { + Error::Builder(String::from( + "could not wrap key with Aes192 key wrap algorithm", + )) + }), + WrappingKey::Aes256(kek) => Kek::from(kek) + .wrap(target_key, self.wrapped_key.as_mut()) + .map_err(|_| { + Error::Builder(String::from( + "could not wrap key with Aes256 key wrap algorithm", + )) + }), + } + } +} +impl AsMut<[u8]> for KeyWrapper { + fn as_mut(&mut self) -> &mut [u8] { + self.wrapping_key.as_mut() + } +} +impl From for Vec { + fn from(wrapper: KeyWrapper) -> Self { + Self::from(wrapper.wrapped_key) + } +} + +/// Represents a wrapping key to be used by [KeyWrapper] +/// +/// This type can be used to abstract over wrapping-key material of different size. +/// The following wrapping key type are currently supported: +/// - Aes128 +/// - Aes192 +/// - Aes256 +/// +#[derive(Debug, Clone, Copy)] +enum WrappingKey { + /// id-aes128-wrap + Aes128([u8; 16]), + /// id-aes192-wrap + Aes192([u8; 24]), + /// id-aes256-wrap + Aes256([u8; 32]), +} +impl From<&KeyWrapAlgorithm> for WrappingKey { + fn from(kw_algo: &KeyWrapAlgorithm) -> Self { + match kw_algo { + KeyWrapAlgorithm::Aes128 => Self::Aes128([0u8; 16]), + KeyWrapAlgorithm::Aes192 => Self::Aes192([0u8; 24]), + KeyWrapAlgorithm::Aes256 => Self::Aes256([0u8; 32]), + } + } +} +impl AsMut<[u8]> for WrappingKey { + fn as_mut(&mut self) -> &mut [u8] { + match self { + Self::Aes128(key) => key, + Self::Aes192(key) => key, + Self::Aes256(key) => key, + } + } +} +/// Represents a wrapped key to be used by [KeyWrapper] +/// +/// This type can be used to abstract over wrapped key of different size for aes-key-wrap algorithms. +/// It currently supports the following incoming key size: +/// - 16 +/// - 24 +/// - 32 +#[derive(Debug, Clone, Copy)] +enum WrappedKey { + Aes128([u8; 24]), + Aes192([u8; 32]), + Aes256([u8; 40]), +} +impl TryFrom for WrappedKey { + type Error = Error; + fn try_from(key_size: usize) -> Result { + match key_size { + 16 => Ok(Self::Aes128([0u8; 24])), + 24 => Ok(Self::Aes192([0u8; 32])), + 32 => Ok(Self::Aes256([0u8; 40])), + _ => Err(Error::Builder(String::from( + "could not wrap key: key size is not supported", + ))), + } + } +} +impl AsMut<[u8]> for WrappedKey { + fn as_mut(&mut self) -> &mut [u8] { + match self { + Self::Aes128(key) => key, + Self::Aes192(key) => key, + Self::Aes256(key) => key, + } + } +} +impl From for Vec { + fn from(key: WrappedKey) -> Self { + match key { + WrappedKey::Aes128(arr) => arr.to_vec(), + WrappedKey::Aes192(arr) => arr.to_vec(), + WrappedKey::Aes256(arr) => arr.to_vec(), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + use alloc::string::ToString; + + #[test] + fn test_keywrapalgorithm_oid() { + assert_eq!( + KeyWrapAlgorithm::Aes128.oid(), + const_oid::db::rfc5911::ID_AES_128_WRAP + ); + assert_eq!( + KeyWrapAlgorithm::Aes192.oid(), + const_oid::db::rfc5911::ID_AES_192_WRAP + ); + assert_eq!( + KeyWrapAlgorithm::Aes256.oid(), + const_oid::db::rfc5911::ID_AES_256_WRAP + ); + } + + #[test] + fn test_keywrapalgorithm_parameters() { + assert_eq!(KeyWrapAlgorithm::Aes128.parameters(), None); + assert_eq!(KeyWrapAlgorithm::Aes192.parameters(), None); + assert_eq!(KeyWrapAlgorithm::Aes256.parameters(), None); + } + + #[test] + fn test_keywrapalgorithm_key_size_in_bits() { + assert_eq!(KeyWrapAlgorithm::Aes128.key_size_in_bits(), 128); + assert_eq!(KeyWrapAlgorithm::Aes192.key_size_in_bits(), 192); + assert_eq!(KeyWrapAlgorithm::Aes256.key_size_in_bits(), 256); + } + + #[test] + fn test_wrappingkey_from_keywrapalgorithm() {} + + #[test] + fn test_algorithmidentifierowned_from_keywrapalgorithm() { + assert_eq!( + AlgorithmIdentifierOwned::from(KeyWrapAlgorithm::Aes128), + AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5911::ID_AES_128_WRAP, + parameters: None, + } + ); + assert_eq!( + AlgorithmIdentifierOwned::from(KeyWrapAlgorithm::Aes192), + AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5911::ID_AES_192_WRAP, + parameters: None, + } + ); + assert_eq!( + AlgorithmIdentifierOwned::from(KeyWrapAlgorithm::Aes256), + AlgorithmIdentifierOwned { + oid: const_oid::db::rfc5911::ID_AES_256_WRAP, + parameters: None, + } + ) + } + + #[test] + fn test_keywrapper_try_new() { + assert!(KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, 10).is_err()); + assert!(KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, 16).is_ok()); + assert!(KeyWrapper::try_new(&KeyWrapAlgorithm::Aes192, 24).is_ok()); + assert!(KeyWrapper::try_new(&KeyWrapAlgorithm::Aes256, 32).is_ok()); + } + + fn fake_kdf(_in: &[u8], _out: &mut impl AsMut<[u8]>) {} + + #[test] + fn test_keywrapper_try_wrap() { + // Key to wrap + let key_to_wrap = [0u8; 16]; + + // Shared secret + let shared_secret = [1u8; 16]; + + // Define a key wrapper from Aes128-key-wrap and key-to-wrap size + let mut key_wrapper = + KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, key_to_wrap.len()).unwrap(); + + // Derive shared key - one can call .as_mut() on key_wrapper + fake_kdf(&shared_secret, &mut key_wrapper); + + // Wrap the key + let r: core::result::Result<(), Error> = key_wrapper.try_wrap(&key_to_wrap); + + assert!(r.is_ok()); + let wrapped_key = Vec::from(key_wrapper); + assert_eq!( + wrapped_key, + alloc::vec![ + 191, 59, 119, 181, 233, 12, 170, 159, 80, 9, 254, 150, 38, 228, 239, 226, 13, 237, + 117, 238, 59, 26, 192, 213 + ] + ) + } + + #[test] + fn test_keywrapper_try_wrap_error() { + // Key to wrap + let key_to_wrap = [0u8; 16]; + + // Define a key wrapper with unsupported key size + assert_eq!( + KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, 15) + .unwrap_err() + .to_string(), + "builder error: could not wrap key: key size is not supported" + ); + + // Define a key wrapper from Aes128-key-wrap but with a different size than key-to-wrap + let mut key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, 24).unwrap(); + + // Wrap the key + assert_eq!( + key_wrapper.try_wrap(&key_to_wrap).unwrap_err().to_string(), + "builder error: could not wrap key with Aes128 key wrap algorithm" + ); + } + + #[test] + fn test_keywrapper_as_mut() { + let mut key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, 16).unwrap(); + let slice_a128 = key_wrapper.as_mut(); + assert_eq!(slice_a128.len(), 16); + assert_eq!(slice_a128[0], 0); + } + + #[test] + fn test_vecu8_from_keywrapper() { + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, 16).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 24); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes192, 16).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 24); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes256, 16).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 24); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes192, 24).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 32); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes192, 24).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 32); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes192, 24).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 32); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes128, 32).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 40); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes192, 32).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 40); + assert_eq!(vec[0], 0); + + let key_wrapper = KeyWrapper::try_new(&KeyWrapAlgorithm::Aes256, 32).unwrap(); + let vec = Vec::from(key_wrapper); + assert_eq!(vec.len(), 40); + assert_eq!(vec[0], 0); + } + + #[test] + fn test_wrappingkey_as_mut() { + let mut key_a128 = WrappingKey::Aes128([0; 16]); + let slice_a128 = key_a128.as_mut(); + assert_eq!(slice_a128.len(), 16); + assert_eq!(slice_a128[0], 0); + + let mut key_a192 = WrappingKey::Aes192([0; 24]); + let slice_a192 = key_a192.as_mut(); + assert_eq!(slice_a192.len(), 24); + assert_eq!(slice_a192[0], 0); + + let mut key_a256 = WrappingKey::Aes256([0; 32]); + let slice_a256 = key_a256.as_mut(); + assert_eq!(slice_a256.len(), 32); + assert_eq!(slice_a256[0], 0); + } + + #[test] + fn test_wrappedkey_from_usize() { + let key_size: usize = 16; + assert!(WrappedKey::try_from(key_size).is_ok()); + + let key_size: usize = 24; + assert!(WrappedKey::try_from(key_size).is_ok()); + + let key_size: usize = 32; + assert!(WrappedKey::try_from(key_size).is_ok()); + + let key_size: usize = 0; + assert!(WrappedKey::try_from(key_size).is_err()); + } + #[test] + fn test_wrappedkey_as_mut() { + let mut key_a128 = WrappedKey::Aes128([0; 24]); + let slice_a128 = key_a128.as_mut(); + assert_eq!(slice_a128.len(), 24); + assert_eq!(slice_a128[0], 0); + + let mut key_a192 = WrappedKey::Aes192([0; 32]); + let slice_a192 = key_a192.as_mut(); + assert_eq!(slice_a192.len(), 32); + assert_eq!(slice_a192[0], 0); + + let mut key_a256 = WrappedKey::Aes256([0; 40]); + let slice_a256 = key_a256.as_mut(); + assert_eq!(slice_a256.len(), 40); + assert_eq!(slice_a256[0], 0); + } + + #[test] + fn test_vecu8_from_wrappedkey() { + let key_a128 = WrappedKey::Aes128([0; 24]); + let vec = Vec::from(key_a128); + assert_eq!(vec.len(), 24); + + let key_a192 = WrappedKey::Aes192([0; 32]); + let vec = Vec::from(key_a192); + assert_eq!(vec.len(), 32); + + let key_a256 = WrappedKey::Aes256([0; 40]); + let vec = Vec::from(key_a256); + assert_eq!(vec.len(), 40); + } +} From 8105d278a41db6706c58809e69c5d2e939026229 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:10:50 -0400 Subject: [PATCH 15/24] cms: ecc-kari support - add p256-priv.der corresponding public key --- cms/tests/examples/p256-pub.der | Bin 0 -> 91 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 cms/tests/examples/p256-pub.der diff --git a/cms/tests/examples/p256-pub.der b/cms/tests/examples/p256-pub.der new file mode 100644 index 0000000000000000000000000000000000000000..67c719c7641d6bc2b1b122bcd287e2947daed3d4 GIT binary patch literal 91 zcmXqrG!SNE*J|@PXUoLM#sOw9GqN)~F|f$2`M)(@U+4Xext*`gz112;Re~CH-z}Ia u#@1+l!}5Ink;Wx1lMH<8zGWR0-}kS1#f5&+c Date: Sun, 20 Oct 2024 09:12:41 -0400 Subject: [PATCH 16/24] cms: ecc-kari support - add comments and exports --- cms/src/builder/utils.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/cms/src/builder/utils.rs b/cms/src/builder/utils.rs index e69de29bb..140b2234a 100644 --- a/cms/src/builder/utils.rs +++ b/cms/src/builder/utils.rs @@ -0,0 +1,12 @@ +//! Utilities module +//! +//! Contains various utilities used during KARI building. +//! It currently contains: +//! - kw: AES Key Wrap +//! - kdf: KDF using ANSI-x9.63 Key Derivation Function + +mod kdf; +pub(super) mod kw; + +pub(super) use kdf::{try_ansi_x963_kdf, HashDigest}; +pub(super) use kw::KeyWrapper; From 804ebf44e2eae33448b59b9f5c4099bf27db5dcf Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:15:36 -0400 Subject: [PATCH 17/24] cms: ecc-kari support - add kari test module --- cms/tests/builder.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/cms/tests/builder.rs b/cms/tests/builder.rs index b126c32e9..89870d530 100644 --- a/cms/tests/builder.rs +++ b/cms/tests/builder.rs @@ -28,6 +28,10 @@ use spki::AlgorithmIdentifierOwned; use x509_cert::attr::{Attribute, AttributeValue}; use x509_cert::serial_number::SerialNumber; +// Modules +#[path = "builder/kari.rs"] +mod kari; + // TODO bk replace this by const_oid definitions as soon as released const RFC8894_ID_MESSAGE_TYPE: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.113733.1.9.2"); From d3015cb5a8edc6be32ff3c2d66c3e18302df47fe Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:16:59 -0400 Subject: [PATCH 18/24] cms: ecc-kari support - add test for kari builder --- cms/tests/builder/kari.rs | 87 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 cms/tests/builder/kari.rs diff --git a/cms/tests/builder/kari.rs b/cms/tests/builder/kari.rs new file mode 100644 index 000000000..503c4c914 --- /dev/null +++ b/cms/tests/builder/kari.rs @@ -0,0 +1,87 @@ +use cms::{ + builder::{ + ContentEncryptionAlgorithm, EcKeyEncryptionInfo, EnvelopedDataBuilder, + KeyAgreeRecipientInfoBuilder, KeyAgreementAlgorithm, KeyWrapAlgorithm, + }, + cert::IssuerAndSerialNumber, + content_info::ContentInfo, + enveloped_data::KeyAgreeRecipientIdentifier, +}; +use der::{Any, AnyRef, Encode}; +use p256::{pkcs8::DecodePrivateKey, SecretKey}; +use pem_rfc7468::LineEnding; +use rand::rngs::OsRng; +use x509_cert::serial_number::SerialNumber; + +fn key_agreement_recipient_identifier(id: i32) -> KeyAgreeRecipientIdentifier { + let issuer = format!("CN=test client {id}").parse().unwrap(); + KeyAgreeRecipientIdentifier::IssuerAndSerialNumber(IssuerAndSerialNumber { + issuer, + serial_number: SerialNumber::new(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06]) + .expect("failed to create a serial number"), + }) +} + +/// Generate a CMS message encrypted with recipient public EC key +/// +/// Can be decrypted using: +/// ```bash +/// openssl cms -decrypt -inkey cms/tests/examples/p256-priv.der -inform PEM +/// ``` +#[test] +fn test_build_enveloped_data_ec() { + // Recipient identifier + let key_agreement_recipient_identifier = key_agreement_recipient_identifier(1); + + // Recipient key material + let recipient_private_key_der = include_bytes!("../examples/p256-priv.der"); + let recipient_private_key = SecretKey::from_pkcs8_der(recipient_private_key_der) + .expect("could not parse in private key"); + let recipient_public_key = recipient_private_key.public_key(); + + // KARI builder + let mut rng = OsRng; + let kari_builder = KeyAgreeRecipientInfoBuilder::new( + None, + key_agreement_recipient_identifier, + EcKeyEncryptionInfo::Ec(recipient_public_key), + KeyAgreementAlgorithm::SinglePassStdDhSha256Kdf, + KeyWrapAlgorithm::Aes128, + &mut rng, + ) + .expect("Could not create a KeyAgreeRecipientInfoBuilder"); + + // Enveloped data builder + let mut rng = OsRng; + let mut builder = EnvelopedDataBuilder::new( + None, + "Arbitrary unencrypted content, encrypted using ECC".as_bytes(), + ContentEncryptionAlgorithm::Aes128Cbc, + None, + ) + .expect("Could not create an EnvelopedData builder."); + + // Enveloped data + let enveloped_data = builder + .add_recipient_info(kari_builder) + .expect("Could not add a recipient info") + .build_with_rng(&mut rng) + .expect("Building EnvelopedData failed"); + let enveloped_data_der = enveloped_data + .to_der() + .expect("conversion of enveloped data to DER failed."); + + // Content info + let content = AnyRef::try_from(enveloped_data_der.as_slice()).unwrap(); + let content_info = ContentInfo { + content_type: const_oid::db::rfc5911::ID_ENVELOPED_DATA, + content: Any::from(content), + }; + let content_info_der = content_info.to_der().unwrap(); + + println!( + "{}", + pem_rfc7468::encode_string("CMS", LineEnding::LF, &content_info_der) + .expect("PEM encoding of enveloped data DER failed") + ); +} From 9bce42a49b5950da616a48bbc9a94921a53e98db Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:21:53 -0400 Subject: [PATCH 19/24] cms: ecc-kari support - Re-organize imports - Adjust comments - Add KeyAgreeRecipientInfoBuilder build logic - Add tests for KeyAgreementAlgorithm and EcKeyEncryptionInfo --- cms/src/builder/kari.rs | 321 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 314 insertions(+), 7 deletions(-) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index 157786670..3eee14d13 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -6,10 +6,39 @@ //! [RFC 5753]: https://datatracker.ietf.org/doc/html/rfc5753 //! -use super::{utils::HashDigest, AlgorithmIdentifierOwned, UserKeyingMaterial}; -use const_oid::ObjectIdentifier; -use der::{asn1::OctetString, Any, Sequence}; -use elliptic_curve::{CurveArithmetic, PublicKey}; +// Super imports +use super::{ + utils::{try_ansi_x963_kdf, HashDigest, KeyWrapper}, + AlgorithmIdentifierOwned, CryptoRngCore, KeyWrapAlgorithm, RecipientInfoBuilder, + RecipientInfoType, Result, UserKeyingMaterial, +}; + +// Crate imports +use crate::{ + content_info::CmsVersion, + enveloped_data::{ + EncryptedKey, KeyAgreeRecipientIdentifier, KeyAgreeRecipientInfo, + OriginatorIdentifierOrKey, OriginatorPublicKey, RecipientEncryptedKey, RecipientInfo, + }, +}; + +// Internal imports +use const_oid::{AssociatedOid, ObjectIdentifier}; +use der::{ + asn1::{BitString, OctetString}, + Any, Decode, Encode, Sequence, +}; + +// Alloc imports +use alloc::{vec, vec::Vec}; + +// RustCrypto imports +use elliptic_curve::{ + ecdh::EphemeralSecret, + point::PointCompression, + sec1::{FromEncodedPoint, ModulusSize, ToEncodedPoint}, + AffinePoint, CurveArithmetic, FieldBytesSize, PublicKey, +}; /// The `EccCmsSharedInfo` type is defined in [RFC 5753 Section 7.2]. /// @@ -65,6 +94,8 @@ pub struct EccCmsSharedInfo { /// /// [RFC 5753 Section 7.1.4]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.1.4 /// [RFC 5753 Section 8]: https://datatracker.ietf.org/doc/html/rfc5753#section-8 +#[allow(clippy::enum_variant_names)] +#[derive(Clone, Copy)] pub enum KeyAgreementAlgorithm { /// dhSinglePass-stdDH-sha224kdf-scheme SinglePassStdDhSha224Kdf, @@ -116,16 +147,16 @@ where } impl EcKeyEncryptionInfo where - C: CurveArithmetic + const_oid::AssociatedOid, + C: CurveArithmetic + AssociatedOid, { - /// Returns the OID associated with the curve used in this `EcKeyEncryptionInfo`. + /// Returns the OID associated with the curve used. pub fn get_oid(&self) -> ObjectIdentifier { C::OID } } impl From<&EcKeyEncryptionInfo> for AlgorithmIdentifierOwned where - C: CurveArithmetic + const_oid::AssociatedOid, + C: CurveArithmetic + AssociatedOid, { fn from(ec_key_encryption_info: &EcKeyEncryptionInfo) -> Self { let parameters = Some(Any::from(&ec_key_encryption_info.get_oid())); @@ -135,3 +166,279 @@ where } } } + +/// Builds a `KeyAgreeRecipientInfo` according to RFC 5652 § 6. +/// This type uses key agreement: the recipient's public key and the sender's +/// private key are used to generate a pairwise symmetric key, then +/// the content-encryption key is encrypted in the pairwise symmetric key. +pub struct KeyAgreeRecipientInfoBuilder<'a, R, C> +where + R: CryptoRngCore, + C: CurveArithmetic, +{ + /// Optional information which helps generating different keys every time. + pub ukm: Option, + /// Encryption algorithm to be used for key encryption + pub rid: KeyAgreeRecipientIdentifier, + /// Recipient key info + pub eckey_encryption_info: EcKeyEncryptionInfo, + /// Content encryption algorithm + pub key_agreement_algorithm: KeyAgreementAlgorithm, + /// Content encryption algorithm + pub key_wrap_algorithm: KeyWrapAlgorithm, + /// Rng + rng: &'a mut R, +} + +impl<'a, R, C> KeyAgreeRecipientInfoBuilder<'a, R, C> +where + R: CryptoRngCore, + C: CurveArithmetic, +{ + /// Creates a `KeyAgreeRecipientInfoBuilder` + pub fn new( + ukm: Option, + rid: KeyAgreeRecipientIdentifier, + eckey_encryption_info: EcKeyEncryptionInfo, + key_agreement_algorithm: KeyAgreementAlgorithm, + key_wrap_algorithm: KeyWrapAlgorithm, + rng: &'a mut R, + ) -> Result> { + Ok(KeyAgreeRecipientInfoBuilder { + ukm, + eckey_encryption_info, + key_agreement_algorithm, + key_wrap_algorithm, + rid, + rng, + }) + } +} +impl<'a, R, C> RecipientInfoBuilder for KeyAgreeRecipientInfoBuilder<'a, R, C> +where + R: CryptoRngCore, + C: CurveArithmetic + AssociatedOid + PointCompression, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldBytesSize: ModulusSize, +{ + /// Returns the RecipientInfoType + fn recipient_info_type(&self) -> RecipientInfoType { + RecipientInfoType::Kari + } + + /// Returns the `CMSVersion` for this `RecipientInfo` + fn recipient_info_version(&self) -> CmsVersion { + CmsVersion::V3 + } + + /// Build a `KeyAgreeRecipientInfo` as per [RFC 5652 Section 6.2.2] and [RFC 5753 Section 3]. + /// + /// For now only [EnvelopedData] using `(ephemeral-static) ECDH` is supported - [RFC 5753 Section 3.1.1] + /// + /// We follow the flow outlined in - [RFC 5753 Section 3.1.2]: + /// + /// Todo: + /// - Add support for `'Co-factor' ECDH` - see [RFC 5753 Section 3.1.1] + /// - Add support for `1-Pass ECMQV` - see [RFC 5753 Section 3.2.1] + /// + /// [RFC 5753 Section 3]: https://datatracker.ietf.org/doc/html/rfc5753#section-3 + /// [RFC 5652 Section 6.2.2]: https://datatracker.ietf.org/doc/html/rfc5652#section-6.2.2 + /// [RFC 5753 Section 3.1.1]: https://datatracker.ietf.org/doc/html/rfc5753#section-3.1.1 + /// [RFC 5753 Section 3.1.2]: https://datatracker.ietf.org/doc/html/rfc5753#section-3.1.2 + /// [RFC 5753 Section 3.2.1]: https://datatracker.ietf.org/doc/html/rfc5753#section-3.2.1 + fn build(&mut self, content_encryption_key: &[u8]) -> Result { + // Encrypt key + let ( + encrypted_key, + ephemeral_pubkey_encoded_point, + originator_algorithm_identifier, + key_encryption_algorithm_identifier, + ) = match self.eckey_encryption_info { + EcKeyEncryptionInfo::Ec(recipient_public_key) => { + // Generate ephemeral key using ecdh + let ephemeral_secret = EphemeralSecret::random(self.rng); + let ephemeral_public_key_encoded_point = + ephemeral_secret.public_key().to_encoded_point(false); + + // Compute a shared secret with recipient public key. Non-uniformly random, but will be used as input for KDF later. + let non_uniformly_random_shared_secret = + ephemeral_secret.diffie_hellman(&recipient_public_key); + let non_uniformly_random_shared_secret_bytes = + non_uniformly_random_shared_secret.raw_secret_bytes(); + + // Generate shared info for KDF + // As per https://datatracker.ietf.org/doc/html/rfc5753#section-7.2" + // ``` + // keyInfo contains the object identifier of the key-encryption + // algorithm (used to wrap the CEK) and associated parameters. In + // this specification, 3DES wrap has NULL parameters while the AES + // wraps have absent parameters. + // ``` + let key_wrap_algorithm_identifier: AlgorithmIdentifierOwned = + self.key_wrap_algorithm.into(); + let key_wrap_algorithm_der = key_wrap_algorithm_identifier.to_der()?; + + // As per https://datatracker.ietf.org/doc/html/rfc5753#section-7.2" + // ``` + // entityUInfo optionally contains additional keying material + // supplied by the sending agent. When used with ECDH and CMS, the + // entityUInfo field contains the octet string ukm. When used with + // ECMQV and CMS, the entityUInfo contains the octet string addedukm + // (encoded in MQVuserKeyingMaterial). + // ``` + let entity_u_info = self.ukm.clone(); + + // As per https://datatracker.ietf.org/doc/html/rfc5753#section-7.2" + // ``` + // suppPubInfo contains the length of the generated KEK, in bits, + // represented as a 32-bit number, as in [CMS-DH] and [CMS-AES]. + // (For example, for AES-256 it would be 00 00 01 00.) + // ``` + let key_wrap_algo_keysize_bits_in_be_bytes: [u8; 4] = + self.key_wrap_algorithm.key_size_in_bits().to_be_bytes(); + + let shared_info = EccCmsSharedInfo { + key_info: key_wrap_algorithm_identifier, + entity_u_info, + supp_pub_info: OctetString::new(key_wrap_algo_keysize_bits_in_be_bytes)?, + }; + let shared_info_der = shared_info.to_der()?; + + // Init a wrapping key (KEK) based on KeyWrapAlgorithm and on CEK (i.e. key to wrap) size + let mut key_wrapper = + KeyWrapper::try_new(&self.key_wrap_algorithm, content_encryption_key.len())?; + + // Derive the Key Encryption Key (KEK) from Shared Secret using ANSI X9.63 KDF + let digest = HashDigest::from(&self.key_agreement_algorithm); + try_ansi_x963_kdf( + non_uniformly_random_shared_secret_bytes.as_slice(), + &shared_info_der, + &mut key_wrapper, + &digest, + )?; + + // Wrap the Content Encryption Key (CEK) with the KEK + key_wrapper.try_wrap(content_encryption_key)?; + + // Return data + ( + Vec::from(key_wrapper), + ephemeral_public_key_encoded_point, + AlgorithmIdentifierOwned::from(&self.eckey_encryption_info), + AlgorithmIdentifierOwned { + oid: self.key_agreement_algorithm.oid(), + parameters: Some(Any::from_der(&key_wrap_algorithm_der)?), + }, + ) + } + }; + + // Build RecipientInfo + Ok(RecipientInfo::Kari(KeyAgreeRecipientInfo { + originator: OriginatorIdentifierOrKey::OriginatorKey(OriginatorPublicKey { + algorithm: originator_algorithm_identifier, + public_key: BitString::from_bytes(ephemeral_pubkey_encoded_point.as_bytes())?, + }), + version: self.recipient_info_version(), + ukm: self.ukm.clone(), + key_enc_alg: key_encryption_algorithm_identifier, + recipient_enc_keys: vec![RecipientEncryptedKey { + rid: self.rid.clone(), + enc_key: EncryptedKey::new(encrypted_key)?, + }], + })) + } +} + +#[cfg(test)] +mod tests { + use std::eprintln; + + use super::*; + use p256::{pkcs8::DecodePublicKey, NistP256, PublicKey}; + + /// Generate a test P256 EcKeyEncryptionInfo + fn get_test_ec_key_info() -> EcKeyEncryptionInfo { + // Public key der bytes: + // ```rust + // let public_key_der_bytes = include_bytes!("../../tests/examples/p256-pub.der"); + // ``` + // OR + // ```bash + // od -An -vtu1 cms/tests/examples/p256-pub.der | tr -s ' ' | tr -d '\n' | sed 's/ /, /g' | sed 's/^, //' |xargs + // ``` + let public_key_der_bytes: &[u8] = &[ + 48, 89, 48, 19, 6, 7, 42, 134, 72, 206, 61, 2, 1, 6, 8, 42, 134, 72, 206, 61, 3, 1, 7, + 3, 66, 0, 4, 28, 172, 255, 181, 95, 47, 44, 239, 216, 157, 137, 235, 55, 75, 38, 129, + 21, 36, 82, 128, 45, 238, 160, 153, 22, 6, 129, 55, 216, 57, 207, 127, 196, 129, 164, + 68, 146, 48, 77, 126, 246, 106, 193, 23, 190, 254, 131, 168, 208, 143, 21, 95, 43, 82, + 249, 246, 24, 221, 68, 112, 41, 4, 142, 15, + ]; + let p256_public_key = PublicKey::from_public_key_der(public_key_der_bytes) + .map_err(|e| eprintln!("{}", e)) + .expect("Getting PublicKey failed"); + EcKeyEncryptionInfo::Ec(p256_public_key) + } + + #[test] + fn test_keyagreementalgorithm_oid() { + assert_eq!( + KeyAgreementAlgorithm::SinglePassStdDhSha224Kdf.oid(), + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_224_KDF_SCHEME + ); + assert_eq!( + KeyAgreementAlgorithm::SinglePassStdDhSha256Kdf.oid(), + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_256_KDF_SCHEME + ); + assert_eq!( + KeyAgreementAlgorithm::SinglePassStdDhSha384Kdf.oid(), + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_384_KDF_SCHEME + ); + assert_eq!( + KeyAgreementAlgorithm::SinglePassStdDhSha512Kdf.oid(), + const_oid::db::rfc5753::DH_SINGLE_PASS_STD_DH_SHA_512_KDF_SCHEME + ); + } + + #[test] + fn test_from_keyagreementalgorithm_for_hashdigest() { + assert_eq!( + HashDigest::from(&KeyAgreementAlgorithm::SinglePassStdDhSha224Kdf), + HashDigest::Sha224 + ); + assert_eq!( + HashDigest::from(&KeyAgreementAlgorithm::SinglePassStdDhSha256Kdf), + HashDigest::Sha256 + ); + assert_eq!( + HashDigest::from(&KeyAgreementAlgorithm::SinglePassStdDhSha384Kdf), + HashDigest::Sha384 + ); + assert_eq!( + HashDigest::from(&KeyAgreementAlgorithm::SinglePassStdDhSha512Kdf), + HashDigest::Sha512 + ); + } + + #[test] + fn test_eckeyencryptioninfo_get_oid() { + let ec_key_encryption_info = get_test_ec_key_info(); + assert_eq!( + ec_key_encryption_info.get_oid(), + ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7") + ); + } + + #[test] + fn test_algorithmidentifierowned_from_eckeyencryptioninfo() { + let ec_key_encryption_info = get_test_ec_key_info(); + + assert_eq!( + AlgorithmIdentifierOwned { + oid: ObjectIdentifier::new_unwrap("1.2.840.10045.2.1"), + parameters: Some(ObjectIdentifier::new_unwrap("1.2.840.10045.3.1.7").into()), + }, + AlgorithmIdentifierOwned::from(&ec_key_encryption_info) + ) + } +} From a042e2c6f32a4f69f1417b09b4c32a7c78a263f1 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 09:44:19 -0400 Subject: [PATCH 20/24] cms: ecc-kari support - remove default builder --- cms/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/cms/Cargo.toml b/cms/Cargo.toml index 5371e1485..f35f0a67d 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -48,7 +48,6 @@ tokio = { version = "1.40.0", features = ["macros", "rt"] } x509-cert = { version = "=0.3.0-pre.0", features = ["pem"] } [features] -default = ["builder"] std = ["der/std", "spki/std"] builder = [ "dep:aes", From 457281cc706631346fc77befaf3106b27ab8f428 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:14:50 -0400 Subject: [PATCH 21/24] cms: ecc-kari support - add From for KeyWrapAlgorithm --- cms/src/builder/utils/kw.rs | 31 +++++++++++++++++++++++++++++-- 1 file changed, 29 insertions(+), 2 deletions(-) diff --git a/cms/src/builder/utils/kw.rs b/cms/src/builder/utils/kw.rs index db7b9e825..0d82c9fd7 100644 --- a/cms/src/builder/utils/kw.rs +++ b/cms/src/builder/utils/kw.rs @@ -5,7 +5,7 @@ use alloc::{string::String, vec::Vec}; -use crate::builder::{Error, Result}; +use crate::builder::{ContentEncryptionAlgorithm, Error, Result}; use aes_kw::Kek; use const_oid::ObjectIdentifier; use der::Any; @@ -37,7 +37,7 @@ use spki::AlgorithmIdentifierOwned; /// /// [RFC 5753 Section 8]: https://datatracker.ietf.org/doc/html/rfc5753#section-8 /// [RFC 5753 Section 7.1.5]: https://datatracker.ietf.org/doc/html/rfc5753#section-7.1.5 -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum KeyWrapAlgorithm { /// id-aes128-wrap Aes128, @@ -129,6 +129,18 @@ impl From for AlgorithmIdentifierOwned { } } } +impl From for KeyWrapAlgorithm { + /// Convert a `ContentEncryptionAlgorithm` to a `KeyWrapAlgorithm`. + /// + /// Conversion is done matching encryption strength. + fn from(ce_algo: ContentEncryptionAlgorithm) -> Self { + match ce_algo { + ContentEncryptionAlgorithm::Aes128Cbc => Self::Aes128, + ContentEncryptionAlgorithm::Aes192Cbc => Self::Aes192, + ContentEncryptionAlgorithm::Aes256Cbc => Self::Aes256, + } + } +} /// This struct can be used to perform key wrapping operation. /// @@ -345,6 +357,21 @@ mod tests { } ) } + #[test] + fn test_keywrapalgorithm_from_contentencryptionalgorithm() { + assert_eq!( + KeyWrapAlgorithm::from(ContentEncryptionAlgorithm::Aes128Cbc), + KeyWrapAlgorithm::Aes128 + ); + assert_eq!( + KeyWrapAlgorithm::from(ContentEncryptionAlgorithm::Aes192Cbc), + KeyWrapAlgorithm::Aes192 + ); + assert_eq!( + KeyWrapAlgorithm::from(ContentEncryptionAlgorithm::Aes256Cbc), + KeyWrapAlgorithm::Aes256 + ); + } #[test] fn test_keywrapper_try_new() { From 4901a07dcf8ebeae44013af90b742b159ee71b12 Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 10:21:12 -0400 Subject: [PATCH 22/24] cms: ecc-kari support - add From for WrappingKey test --- cms/src/builder/utils/kw.rs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/cms/src/builder/utils/kw.rs b/cms/src/builder/utils/kw.rs index 0d82c9fd7..32cf3a349 100644 --- a/cms/src/builder/utils/kw.rs +++ b/cms/src/builder/utils/kw.rs @@ -222,7 +222,7 @@ impl From for Vec { /// - Aes192 /// - Aes256 /// -#[derive(Debug, Clone, Copy)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] enum WrappingKey { /// id-aes128-wrap Aes128([u8; 16]), @@ -330,9 +330,6 @@ mod tests { assert_eq!(KeyWrapAlgorithm::Aes256.key_size_in_bits(), 256); } - #[test] - fn test_wrappingkey_from_keywrapalgorithm() {} - #[test] fn test_algorithmidentifierowned_from_keywrapalgorithm() { assert_eq!( @@ -491,6 +488,22 @@ mod tests { assert_eq!(vec[0], 0); } + #[test] + fn test_wrappingkey_from_keywrapalgorithm() { + assert_eq!( + WrappingKey::from(&KeyWrapAlgorithm::Aes128), + WrappingKey::Aes128([0u8; 16]) + ); + assert_eq!( + WrappingKey::from(&KeyWrapAlgorithm::Aes192), + WrappingKey::Aes192([0u8; 24]) + ); + assert_eq!( + WrappingKey::from(&KeyWrapAlgorithm::Aes256), + WrappingKey::Aes256([0u8; 32]) + ); + } + #[test] fn test_wrappingkey_as_mut() { let mut key_a128 = WrappingKey::Aes128([0; 16]); From fa5063276f3d3522f43b5e71f4ac774dda589afc Mon Sep 17 00:00:00 2001 From: nemynm <180121731+nemynm@users.noreply.github.com> Date: Sun, 20 Oct 2024 11:51:59 -0400 Subject: [PATCH 23/24] cms: ecc-kari support - bring EnvelopedData in scope for doc --- cms/src/builder/kari.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cms/src/builder/kari.rs b/cms/src/builder/kari.rs index 3eee14d13..96f5b8559 100644 --- a/cms/src/builder/kari.rs +++ b/cms/src/builder/kari.rs @@ -14,6 +14,8 @@ use super::{ }; // Crate imports +#[cfg(doc)] +use crate::enveloped_data::EnvelopedData; use crate::{ content_info::CmsVersion, enveloped_data::{ From 73b0b44a3054c1e90240ededd9f76c4b7f54aef4 Mon Sep 17 00:00:00 2001 From: Arthur Gautier Date: Mon, 21 Oct 2024 09:29:29 -0700 Subject: [PATCH 24/24] cms: use aes-kw pre-release --- Cargo.lock | 94 ++++++++++---------------------------------------- Cargo.toml | 8 +++++ cms/Cargo.toml | 4 +-- 3 files changed, 29 insertions(+), 77 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b36dc3af9..588d739c7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -23,18 +23,7 @@ version = "0.6.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5f451b77e2f92932dc411da6ef9f3d33efad68a6f14a7a83e559453458e85ac" dependencies = [ - "crypto-common 0.2.0-rc.1", -] - -[[package]] -name = "aes" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" -dependencies = [ - "cfg-if", - "cipher 0.4.4", - "cpufeatures", + "crypto-common", ] [[package]] @@ -44,7 +33,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e7856582c758ade85d71daf27ec6bcea6c1c73913692b07b8dffea2dc03531c9" dependencies = [ "cfg-if", - "cipher 0.5.0-pre.7", + "cipher", "cpufeatures", ] @@ -55,8 +44,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cce27af05d45b901bb28da33ff8b2b2b2044f595b24fc0f36d4882dae91d484" dependencies = [ "aead", - "aes 0.9.0-pre.2", - "cipher 0.5.0-pre.7", + "aes", + "cipher", "ctr", "ghash", "subtle", @@ -64,11 +53,11 @@ dependencies = [ [[package]] name = "aes-kw" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69fa2b352dcefb5f7f3a5fb840e02665d311d878955380515e4fd50095dd3d8c" +version = "0.3.0-pre" +source = "git+https://github.com/RustCrypto/key-wraps.git#c9437ce4933822252863fd64fc06c631405ad8b1" dependencies = [ - "aes 0.8.4", + "aes", + "const-oid", ] [[package]] @@ -292,7 +281,7 @@ version = "0.2.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0729a0a8422deb6056b8fcd89c42b724fe27e69458fa006f00c63cbffffd91b" dependencies = [ - "cipher 0.5.0-pre.7", + "cipher", ] [[package]] @@ -328,24 +317,14 @@ dependencies = [ "half", ] -[[package]] -name = "cipher" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" -dependencies = [ - "crypto-common 0.1.6", - "inout 0.1.3", -] - [[package]] name = "cipher" version = "0.5.0-pre.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b1425e6ce000f05a73096556cabcfb6a10a3ffe3bb4d75416ca8f00819c0b6a" dependencies = [ - "crypto-common 0.2.0-rc.1", - "inout 0.2.0-rc.1", + "crypto-common", + "inout", ] [[package]] @@ -391,12 +370,12 @@ dependencies = [ name = "cms" version = "0.3.0-pre.0" dependencies = [ - "aes 0.9.0-pre.2", + "aes", "aes-kw", "ansi-x963-kdf", "async-signature", "cbc", - "cipher 0.5.0-pre.7", + "cipher", "const-oid", "der", "ecdsa", @@ -505,16 +484,6 @@ dependencies = [ "zeroize", ] -[[package]] -name = "crypto-common" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] - [[package]] name = "crypto-common" version = "0.2.0-rc.1" @@ -532,7 +501,7 @@ version = "0.10.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77e1482d284b80d7fddb211666d513dc5e23b0cc3a03ad398ff70543827c789f" dependencies = [ - "cipher 0.5.0-pre.7", + "cipher", ] [[package]] @@ -586,7 +555,7 @@ version = "0.9.0-pre.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76239c731adb4b5204cfeec47bd06ec1071d9477a0d32bbb83dc7d8c599efe63" dependencies = [ - "cipher 0.5.0-pre.7", + "cipher", ] [[package]] @@ -597,7 +566,7 @@ checksum = "cf2e3d6615d99707295a9673e889bf363a04b2a466bd320c65a72536f7577379" dependencies = [ "block-buffer", "const-oid", - "crypto-common 0.2.0-rc.1", + "crypto-common", "subtle", ] @@ -781,16 +750,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "getrandom" version = "0.2.15" @@ -911,15 +870,6 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "inout" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" -dependencies = [ - "generic-array", -] - [[package]] name = "inout" version = "0.2.0-rc.1" @@ -1168,7 +1118,7 @@ dependencies = [ name = "pkcs5" version = "0.8.0-rc.2" dependencies = [ - "aes 0.9.0-pre.2", + "aes", "aes-gcm", "cbc", "der", @@ -1494,7 +1444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1affa54a576c40080654b494bb3f3198fa2fe46e0954b85196d122e3561c2fd0" dependencies = [ "cfg-if", - "cipher 0.5.0-pre.7", + "cipher", ] [[package]] @@ -1893,7 +1843,7 @@ version = "0.6.0-rc.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3517d72c5ca6d60f9f2e85d2c772e2652830062a685105a528d19dd823cf87d5" dependencies = [ - "crypto-common 0.2.0-rc.1", + "crypto-common", "subtle", ] @@ -1903,12 +1853,6 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" -[[package]] -name = "version_check" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - [[package]] name = "wait-timeout" version = "0.2.0" diff --git a/Cargo.toml b/Cargo.toml index 85cc8f141..7aaa419e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,3 +58,11 @@ tls_codec_derive = { path = "./tls_codec/derive" } x509-tsp = { path = "./x509-tsp" } x509-cert = { path = "./x509-cert" } x509-ocsp = { path = "./x509-ocsp" } + +# https://github.com/RustCrypto/key-wraps/pull/34 +# https://github.com/RustCrypto/key-wraps/pull/35 +aes-kw = { git = "https://github.com/RustCrypto/key-wraps.git" } + +# https://github.com/RustCrypto/KDFs/pull/102 +ansi-x963-kdf = { git = "https://github.com/RustCrypto/KDFs.git" } + diff --git a/cms/Cargo.toml b/cms/Cargo.toml index f35f0a67d..ff8650d40 100644 --- a/cms/Cargo.toml +++ b/cms/Cargo.toml @@ -22,8 +22,8 @@ x509-cert = { version = "=0.3.0-pre.0", default-features = false } # optional dependencies aes = { version = "=0.9.0-pre.2", optional = true } -aes-kw = { version ="0.2.1", optional = true } -ansi-x963-kdf = { git = "https://github.com/RustCrypto/KDFs.git", version = "0.1.0", optional = true } +aes-kw = { version ="=0.3.0-pre", optional = true } +ansi-x963-kdf = { version = "0.1.0", optional = true } async-signature = { version = "=0.6.0-pre.4", features = ["digest", "rand_core"], optional = true } cbc = { version = "=0.2.0-pre.2", optional = true } cipher = { version = "=0.5.0-pre.7", features = ["alloc", "block-padding", "rand_core"], optional = true }