From 5571f2eea9124d773045938d8c109a00de343867 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sun, 8 Nov 2026 23:01:05 -0800 Subject: [PATCH 1/2] chore(quest): claim quest/m0/adapters/aws-lc-rs.md Quest-Claim: 186aaa4f7adaa222 From 2dbe83d2449a26cbc66cec3ce08189782a36e790 Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Sun, 8 Nov 2026 23:32:34 -0800 Subject: [PATCH 2/2] fix(aws-lc-rs): offer exactly aws-lc-rs 1.x's API open_in_place_separate_tag takes aws-lc-rs's (nonce, aad, &[u8], in_out) rather than ring's, the kem and key_wrap constructors are generic, ring-io and ring-sig-verify gate what they gate upstream, and the cpu module, the pbkdf2::Algorithm Debug impl, and the TryFrom impl upstream lacks are gone. Set to 1.0.3 by hand, as a fix restoring upstream's API. Co-Authored-By: Claude Opus 5.5 --- Cargo.lock | 2 +- MIGRATE.md | 2 +- MIGRATING.md | 15 +++++----- quest/m0/adapters/aws-lc-rs.md | 20 ------------- rind-aws-lc-rs/Cargo.toml | 7 +++-- rind-aws-lc-rs/README.md | 4 +-- rind-aws-lc-rs/src/aead.rs | 10 +++---- rind-aws-lc-rs/src/cipher.rs | 1 + rind-aws-lc-rs/src/cpu.rs | 23 --------------- rind-aws-lc-rs/src/der.rs | 14 ++++----- rind-aws-lc-rs/src/error.rs | 25 ++++++++++++++-- rind-aws-lc-rs/src/kem.rs | 43 +++++++++++++++++----------- rind-aws-lc-rs/src/key_wrap.rs | 18 ++++++------ rind-aws-lc-rs/src/lib.rs | 2 +- rind-aws-lc-rs/src/pbkdf2.rs | 2 +- rind-aws-lc-rs/src/rsa.rs | 20 +++++++++---- rind-aws-lc-rs/tests/differential.rs | 21 +++++++++++++- rind-aws-lc-rs/tests/keys.rs | 12 ++++++-- 18 files changed, 132 insertions(+), 109 deletions(-) delete mode 100644 quest/m0/adapters/aws-lc-rs.md delete mode 100644 rind-aws-lc-rs/src/cpu.rs diff --git a/Cargo.lock b/Cargo.lock index 8a30898ef..bbb46ae1e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3106,7 +3106,7 @@ dependencies = [ [[package]] name = "rind-aws-lc-rs" -version = "1.0.2" +version = "1.0.3" dependencies = [ "aws-lc-rs", "crabgrind 0.4.0", diff --git a/MIGRATE.md b/MIGRATE.md index 6d05e1e3d..2d4af9aca 100644 --- a/MIGRATE.md +++ b/MIGRATE.md @@ -132,7 +132,7 @@ For each way the survey found: rind panics on a CPU missing a feature it requires. At the start of each binary's `main`, refuse one by exiting with the error from an adapter's -`default_provider()`, rind-ring's or rind-aws-lc-rs's `cpu::check()`, +`default_provider()`, rind-ring's `cpu::check()`, rind-rcgen's `check()`, or `rind::cpu::check()`. A library leaves this to the binaries that use it, and says so in its docs. diff --git a/MIGRATING.md b/MIGRATING.md index b32e57ff9..86d8d5caa 100644 --- a/MIGRATING.md +++ b/MIGRATING.md @@ -86,7 +86,7 @@ reuses it. `verify` refuses a stored value shorter than the HMAC output with | `SealingKey`, `OpeningKey`, `BoundKey`, `NonceSequence`, `nonce_sequence::{Counter32, Counter64}` and their builders | `Key`, given a `Nonce` per call from a sequence number the protocol keeps | | `seal_in_place_append_tag(nonce, Aad::from(aad), in_out)` | `seal_in_place_append_tag(nonce, aad, in_out)`, with `aad: &[u8]` | | `seal_in_place_separate_tag`, `open_in_place` | the same, with `aad: &[u8]` | -| `open_in_place_separate_tag(nonce, aad, tag, in_out, range)` | `open_in_place_separate_tag(nonce, aad, Tag::try_from(tag)?, ciphertext)` | +| `open_in_place_separate_tag(nonce, aad, tag, in_out)`, which ring takes as `(nonce, aad, tag, in_out, range)` | `open_in_place_separate_tag(nonce, aad, Tag::try_from(tag)?, ciphertext)` | | `open_within(nonce, aad, in_out, range)` | `open_in_place` on the ciphertext's subslice, which leaves the plaintext there rather than moving it to the front | | `Aad::from(aad)`, `Aad::empty()` | `aad`, `&[]` | | `Nonce::assume_unique_for_key` | the same | @@ -348,11 +348,10 @@ ring has none. Each `rind-*` crate stands in for a crate's aws-lc-rs or ring backend. rind panics on a CPU missing a feature it requires, so every adapter function that returns a provider or ticketer refuses one with the adapter's own -`MissingFeature` instead, which displays rind's message. rind-ring and -rind-aws-lc-rs, whose signatures are ring's and aws-lc-rs's, and rind-rcgen's -`KeyPair`, whose errors are rcgen's, cannot. Call rind-ring's or -rind-aws-lc-rs's `cpu::check()`, or rind-rcgen's `check()`, which returns its -own `MissingFeature`, before using them. +`MissingFeature` instead, which displays rind's message. rind-ring, whose +signatures are ring's, and rind-rcgen's `KeyPair`, whose errors are rcgen's, +cannot. Call rind-ring's `cpu::check()`, or rind-rcgen's `check()`, which +returns its own `MissingFeature`, before using them. ### rind-ring @@ -376,8 +375,8 @@ items it leaves out and the inputs it refuses where ring takes them. rind-aws-lc-rs offers aws-lc-rs 1.x's API name for name for every item rind backs, so a crate on aws-lc-rs swaps it in with `aws-lc-rs = { package = "rind-aws-lc-rs", version = "1" }` and no code changes. The rename reaches -only that crate. It accepts every aws-lc-rs feature but `fips`, and adds the -same `cpu` module as rind-ring. Its [README](rind-aws-lc-rs/README.md) lists +only that crate. It accepts every aws-lc-rs feature but `fips`, and adds only +a `wipe` feature, since aws-lc-rs always wipes. Its [README](rind-aws-lc-rs/README.md) lists the items it leaves out, such as ML-DSA, ML-KEM-512 and ML-KEM-1024, SHA-224, and the PKCS#1 v1.5 encryption keys, and where it behaves differently. rustls's `aws_lc_rs` provider, rustls-webpki's `aws-lc-rs` feature, and rcgen's diff --git a/quest/m0/adapters/aws-lc-rs.md b/quest/m0/adapters/aws-lc-rs.md deleted file mode 100644 index 03c6ea063..000000000 --- a/quest/m0/adapters/aws-lc-rs.md +++ /dev/null @@ -1,20 +0,0 @@ -# [M] rind-aws-lc-rs's signatures are aws-lc-rs 1.x's - -## Goal - -Code written for aws-lc-rs 1.x builds unchanged against rind-aws-lc-rs wherever rind backs the algorithm, and the adapter adds nothing upstream lacks. - -## Plan - -Found by diffing rustdoc JSON against aws-lc-rs 1.19.0 on 2026-11-08: - -- `aead::LessSafeKey::open_in_place_separate_tag` (`src/aead.rs:262`) takes ring's `(nonce, aad, tag: Tag, in_out, ciphertext: RangeFrom)`; aws-lc-rs 1.17 and later take `(nonce, aad, tag: &[u8], in_out)`. Code written for aws-lc-rs fails to build against the adapter, so fix this first. -- `cipher::OperatingMode` and `rsa::KeySize` lack upstream's `#[non_exhaustive]`. -- Remove the `Debug` impl on `pbkdf2::Algorithm`, which upstream lacks. -- Implement `TryInto for PublicKeyComponents` directly, as upstream does, instead of `TryFrom`. -- Add `kem::AlgorithmIdentifier::nid()`, returning AWS-LC's `NID_MLKEM768`. -- Make `kem::DecapsulationKey::{generate, encapsulation_key}`, `EncapsulationKey::new`, and `key_wrap::KeyEncryptionKey::new` generic, as upstream's are. -- Override `Error::description()` and `cause()` as upstream does. -- Remove the `cpu` module (`check` and `MissingFeature`). -- Gate `io`, `VerificationAlgorithm::verify`, and `From` behind upstream's `ring-io` and `ring-sig-verify` features. -- Keep the `wipe` feature, documented in the README: aws-lc-rs always wipes, so rind needs the switch. diff --git a/rind-aws-lc-rs/Cargo.toml b/rind-aws-lc-rs/Cargo.toml index e0d6edb0f..a5eedd34e 100644 --- a/rind-aws-lc-rs/Cargo.toml +++ b/rind-aws-lc-rs/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rind-aws-lc-rs" -version = "1.0.2" +version = "1.0.3" edition = "2024" repository = "https://github.com/moq-dev/rind" license = "Apache-2.0 OR ISC OR MIT-0" @@ -15,8 +15,9 @@ readme = "README.md" [package.metadata.rind] upstream = "1" -# aws-lc-rs's feature names, so a manifest naming them builds unchanged; each -# changes nothing. `fips` is left out, so a manifest asking for it fails to +# aws-lc-rs's feature names, so a manifest naming them builds unchanged. +# `ring-io` and `ring-sig-verify` gate what they gate there; the rest change +# nothing. `fips` is left out, so a manifest asking for it fails to # resolve: rind is not FIPS validated. [features] default = ["aws-lc-sys", "alloc", "ring-io", "ring-sig-verify"] diff --git a/rind-aws-lc-rs/README.md b/rind-aws-lc-rs/README.md index eb552c0e4..d40e57a35 100644 --- a/rind-aws-lc-rs/README.md +++ b/rind-aws-lc-rs/README.md @@ -17,13 +17,14 @@ The rename reaches only the crate that makes it, not dependencies that use aws-l It panics wherever aws-lc-rs panics, such as `pbkdf2::derive` asked for more than PBKDF2 allows, so its signatures stay aws-lc-rs's, as rind-ring's stay ring's. -Every aws-lc-rs feature but `fips` is accepted and changes nothing. +Every aws-lc-rs feature but `fips` is accepted. `ring-io` and `ring-sig-verify` gate the items they gate in aws-lc-rs, and the rest change nothing. `fips` is refused, since rind is not FIPS validated: `try_fips_mode()` always fails and `fips_version()` is `None`. ## Wiping keys Keys, and the adapter's own copies of secrets such as PKCS#8 documents, Ed25519 seeds, and ML-KEM shared secrets, are wiped on drop only under the `wipe` feature, which aws-lc-rs lacks since it always wipes. It turns on rind's own `wipe` feature too. Leaving it off skips the wipe, which is faster. +It is the one item this crate adds to aws-lc-rs's API. ## Not offered @@ -41,7 +42,6 @@ These items are left out, so code using them fails to build: ## Behaves differently -- rind panics on a CPU missing a feature it requires, and aws-lc-rs's signatures have no way to report one, so call [`cpu::check()`] at startup. [`cpu`] is rind's own, which aws-lc-rs lacks. - Where aws-lc-rs takes a [`rand::SecureRandom`] to generate a key or sign, rind draws from the system random source itself, as [`rand::SystemRandom`], the only `SecureRandom`, does. - A [`error::KeyRejected`] from rind's own checks gives rind's reason in its `Display`, and `"InvalidEncoding"` from `description_()`. - `VerificationAlgorithm::verify_digest_sig` and `verify_digest` fail for RSA, as they do for Ed25519: rind verifies RSA only over a message it hashes itself. diff --git a/rind-aws-lc-rs/src/aead.rs b/rind-aws-lc-rs/src/aead.rs index 7ffd9043a..0e689a255 100644 --- a/rind-aws-lc-rs/src/aead.rs +++ b/rind-aws-lc-rs/src/aead.rs @@ -257,20 +257,20 @@ impl LessSafeKey { key.0 } - /// Opens `in_out[ciphertext]` against the separate `tag`, and returns the - /// plaintext, moved to the front of `in_out`. + /// Opens the ciphertext `in_out` against the separate `tag`, and returns + /// the plaintext, written over it. + #[allow(clippy::needless_pass_by_value)] pub fn open_in_place_separate_tag<'in_out, A>( &self, nonce: Nonce, aad: Aad, - tag: Tag, + tag: &[u8], in_out: &'in_out mut [u8], - ciphertext: RangeFrom, ) -> Result<&'in_out mut [u8], error::Unspecified> where A: AsRef<[u8]>, { - self.open(nonce, aad.as_ref(), &tag.0, in_out, ciphertext) + self.open(nonce, aad.as_ref(), tag, in_out, 0..) } fn open<'in_out>( diff --git a/rind-aws-lc-rs/src/cipher.rs b/rind-aws-lc-rs/src/cipher.rs index ee9dcc419..f3bebe123 100644 --- a/rind-aws-lc-rs/src/cipher.rs +++ b/rind-aws-lc-rs/src/cipher.rs @@ -30,6 +30,7 @@ pub const AES_CTR_IV_LEN: usize = 16; const AES_BLOCK_LEN: usize = 16; /// A block cipher mode of operation. +#[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum OperatingMode { /// Cipher block chaining. diff --git a/rind-aws-lc-rs/src/cpu.rs b/rind-aws-lc-rs/src/cpu.rs deleted file mode 100644 index 9731a1992..000000000 --- a/rind-aws-lc-rs/src/cpu.rs +++ /dev/null @@ -1,23 +0,0 @@ -//! rind's CPU check, for refusing an unsupported CPU at startup instead of panicking. -//! -//! aws-lc-rs lacks this module: AWS-LC runs on any CPU, where rind requires -//! the features [PLATFORMS.md](https://github.com/moq-dev/rind/blob/main/PLATFORMS.md) lists. - -use core::fmt; - -/// Checks that this CPU has every feature rind requires, naming the first one missing. -pub fn check() -> Result<(), MissingFeature> { - rind::cpu::check().map_err(MissingFeature) -} - -/// A CPU feature rind requires that this CPU lacks. -#[derive(Debug)] -pub struct MissingFeature(rind::cpu::MissingFeature); - -impl fmt::Display for MissingFeature { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.0.fmt(f) - } -} - -impl std::error::Error for MissingFeature {} diff --git a/rind-aws-lc-rs/src/der.rs b/rind-aws-lc-rs/src/der.rs index ba94db6e1..2015d8d57 100644 --- a/rind-aws-lc-rs/src/der.rs +++ b/rind-aws-lc-rs/src/der.rs @@ -2,7 +2,7 @@ //! a PKCS#1 RSA public key's fields, which rind keeps to itself. rind parses //! and encodes the keys; every byte read here is public. -use crate::error::Unspecified; +use crate::error::{Unspecified, unspecified}; const INTEGER: u8 = 0x02; const SEQUENCE: u8 = 0x30; @@ -13,18 +13,18 @@ fn read<'a>( input: &mut untrusted::Reader<'a>, tag: u8, ) -> Result, Unspecified> { - if input.read_byte()? != tag { + if input.read_byte().map_err(unspecified)? != tag { return Err(Unspecified); } - let len = match input.read_byte()? { + let len = match input.read_byte().map_err(unspecified)? { len @ 0..=0x7f => usize::from(len), - 0x81 => match input.read_byte()? { + 0x81 => match input.read_byte().map_err(unspecified)? { len @ 0x80..=0xff => usize::from(len), _ => return Err(Unspecified), }, 0x82 => { - let high = input.read_byte()?; - let low = input.read_byte()?; + let high = input.read_byte().map_err(unspecified)?; + let low = input.read_byte().map_err(unspecified)?; match u16::from_be_bytes([high, low]) { len @ 0x100..=0xffff => usize::from(len), _ => return Err(Unspecified), @@ -32,7 +32,7 @@ fn read<'a>( } _ => return Err(Unspecified), }; - Ok(input.read_bytes(len)?) + input.read_bytes(len).map_err(unspecified) } /// Reads a positive INTEGER and returns its magnitude, without the zero byte diff --git a/rind-aws-lc-rs/src/error.rs b/rind-aws-lc-rs/src/error.rs index 299c8c018..905591786 100644 --- a/rind-aws-lc-rs/src/error.rs +++ b/rind-aws-lc-rs/src/error.rs @@ -13,7 +13,18 @@ impl fmt::Display for Unspecified { } } -impl std::error::Error for Unspecified {} +// aws-lc-rs overrides the deprecated `description` and `cause`, so a caller +// still on them gets its answers. +#[allow(deprecated)] +impl std::error::Error for Unspecified { + fn description(&self) -> &str { + "Unspecified" + } + + fn cause(&self) -> Option<&dyn std::error::Error> { + None + } +} impl From for Unspecified { fn from(_: core::array::TryFromSliceError) -> Self { @@ -21,6 +32,7 @@ impl From for Unspecified { } } +#[cfg(any(feature = "ring-sig-verify", feature = "ring-io"))] impl From for Unspecified { fn from(_: untrusted::EndOfInput) -> Self { Self @@ -120,7 +132,16 @@ impl fmt::Display for KeyRejected { } } -impl std::error::Error for KeyRejected {} +#[allow(deprecated)] +impl std::error::Error for KeyRejected { + fn description(&self) -> &str { + self.description_() + } + + fn cause(&self) -> Option<&dyn std::error::Error> { + None + } +} impl From<()> for KeyRejected { fn from((): ()) -> Self { diff --git a/rind-aws-lc-rs/src/kem.rs b/rind-aws-lc-rs/src/kem.rs index 4304dc048..2e6e69db4 100644 --- a/rind-aws-lc-rs/src/kem.rs +++ b/rind-aws-lc-rs/src/kem.rs @@ -19,6 +19,8 @@ pub const ML_KEM_768: Algorithm = Algorithm { pub trait AlgorithmIdentifier: Copy + Clone + fmt::Debug + PartialEq + crate::sealed::Sealed + 'static { + /// The algorithm's AWS-LC NID. + fn nid(self) -> i32; } /// A KEM algorithm. @@ -55,14 +57,26 @@ pub enum AlgorithmId { MlKem768, } -impl AlgorithmIdentifier for AlgorithmId {} +/// AWS-LC's `NID_MLKEM768`. +const NID_MLKEM768: i32 = 989; + +impl AlgorithmIdentifier for AlgorithmId { + fn nid(self) -> i32 { + match self { + Self::MlKem768 => NID_MLKEM768, + } + } +} impl crate::sealed::Sealed for AlgorithmId {} -impl AlgorithmId { - fn rind(self) -> &'static rind::kem::Algorithm { - match self { - Self::MlKem768 => &rind::kem::ML_KEM_768, +impl Algorithm { + /// rind's algorithm; only [`AlgorithmId`] implements the sealed + /// [`AlgorithmIdentifier`], so its NID names one this module backs. + fn rind(&self) -> &'static rind::kem::Algorithm { + match self.id.nid() { + NID_MLKEM768 => &rind::kem::ML_KEM_768, + nid => unreachable!("no KEM has NID {nid}"), } } } @@ -79,10 +93,10 @@ where key: rind::kem::DecapsulationKey, } -impl DecapsulationKey { +impl DecapsulationKey { /// Generates a decapsulation key for `alg`. - pub fn generate(alg: &'static Algorithm) -> Result { - let key = rind::kem::DecapsulationKey::generate(alg.id.rind()).map_err(unspecified)?; + pub fn generate(alg: &'static Algorithm) -> Result { + let key = rind::kem::DecapsulationKey::generate(alg.rind()).map_err(unspecified)?; Ok(Self { algorithm: alg, key, @@ -90,7 +104,7 @@ impl DecapsulationKey { } /// The key's encapsulation key. - pub fn encapsulation_key(&self) -> Result, Unspecified> { + pub fn encapsulation_key(&self) -> Result, Unspecified> { Ok(EncapsulationKey { algorithm: self.algorithm, key: self.key.public_key(), @@ -108,9 +122,7 @@ impl DecapsulationKey { .map_err(unspecified)?; Ok(SharedSecret(Wiped::new(secret.as_ref().into()))) } -} -impl DecapsulationKey { /// The key's algorithm. #[must_use] pub fn algorithm(&self) -> &'static Algorithm { @@ -135,16 +147,15 @@ where key: rind::kem::EncapsulationKey, } -impl EncapsulationKey { +impl EncapsulationKey { /// Parses an encapsulation key for `alg` from its bytes. - pub fn new(alg: &'static Algorithm, bytes: &[u8]) -> Result { + pub fn new(alg: &'static Algorithm, bytes: &[u8]) -> Result { match bytes.len().cmp(&alg.encapsulate_key_size) { core::cmp::Ordering::Less => return Err(KeyRejected::too_small()), core::cmp::Ordering::Greater => return Err(KeyRejected::too_large()), core::cmp::Ordering::Equal => {} } - let key = - rind::kem::EncapsulationKey::new(alg.id.rind(), bytes).map_err(KeyRejected::rind)?; + let key = rind::kem::EncapsulationKey::new(alg.rind(), bytes).map_err(KeyRejected::rind)?; Ok(Self { algorithm: alg, key, @@ -165,9 +176,7 @@ impl EncapsulationKey { pub fn key_bytes(&self) -> Result, Unspecified> { Ok(EncapsulationKeyBytes::new(self.key.as_ref().to_vec())) } -} -impl EncapsulationKey { /// The key's algorithm. #[must_use] pub fn algorithm(&self) -> &'static Algorithm { diff --git a/rind-aws-lc-rs/src/key_wrap.rs b/rind-aws-lc-rs/src/key_wrap.rs index a4185fbc6..936951cfa 100644 --- a/rind-aws-lc-rs/src/key_wrap.rs +++ b/rind-aws-lc-rs/src/key_wrap.rs @@ -30,11 +30,11 @@ pub struct AesBlockCipher { key_len: usize, } -impl AesBlockCipher { - fn rind(&self) -> &'static rind::cipher::Algorithm { - match self.id { - BlockCipherId::Aes128 => &rind::key_wrap::AES_128, - BlockCipherId::Aes256 => &rind::key_wrap::AES_256, +impl BlockCipherId { + fn rind(self) -> &'static rind::cipher::Algorithm { + match self { + Self::Aes128 => &rind::key_wrap::AES_128, + Self::Aes256 => &rind::key_wrap::AES_256, } } } @@ -118,16 +118,14 @@ pub struct KeyEncryptionKey { kek: rind::key_wrap::AesKek, } -impl KeyEncryptionKey { +impl KeyEncryptionKey { /// Makes a key encryption key from `key`, which must be `cipher`'s key /// length. - pub fn new(cipher: &'static AesBlockCipher, key: &[u8]) -> Result { - let kek = rind::key_wrap::AesKek::new(cipher.rind(), key).map_err(unspecified)?; + pub fn new(cipher: &'static Cipher, key: &[u8]) -> Result { + let kek = rind::key_wrap::AesKek::new(cipher.id().rind(), key).map_err(unspecified)?; Ok(Self { cipher, kek }) } -} -impl KeyEncryptionKey { /// The block cipher's identity. pub fn block_cipher_id(&self) -> BlockCipherId { self.cipher.id() diff --git a/rind-aws-lc-rs/src/lib.rs b/rind-aws-lc-rs/src/lib.rs index 62d1165fb..ec6a88bf9 100644 --- a/rind-aws-lc-rs/src/lib.rs +++ b/rind-aws-lc-rs/src/lib.rs @@ -11,12 +11,12 @@ pub mod aead; pub mod agreement; pub mod cipher; pub mod constant_time; -pub mod cpu; pub mod digest; pub mod encoding; pub mod error; pub mod hkdf; pub mod hmac; +#[cfg(feature = "ring-io")] pub mod io; pub mod iv; pub mod kem; diff --git a/rind-aws-lc-rs/src/pbkdf2.rs b/rind-aws-lc-rs/src/pbkdf2.rs index 3130f5719..ac3407a18 100644 --- a/rind-aws-lc-rs/src/pbkdf2.rs +++ b/rind-aws-lc-rs/src/pbkdf2.rs @@ -5,7 +5,7 @@ use core::num::NonZeroU32; use crate::{digest, error, hmac}; /// A PBKDF2 algorithm, chosen by one of this module's constants. -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, PartialEq, Eq)] pub struct Algorithm(hmac::Algorithm); impl Algorithm { diff --git a/rind-aws-lc-rs/src/rsa.rs b/rind-aws-lc-rs/src/rsa.rs index 7c7d1e9c7..35fb2f4c6 100644 --- a/rind-aws-lc-rs/src/rsa.rs +++ b/rind-aws-lc-rs/src/rsa.rs @@ -20,12 +20,15 @@ use crate::wipe::Wiped; use crate::digest::{self, Digest}; use crate::encoding::{AsDer, Pkcs8V1Der, PublicKeyX509Der}; use crate::error::{KeyRejected, Unspecified, unspecified}; +#[cfg(feature = "ring-io")] +use crate::io; use crate::signature::{self, ParsedPublicKey, private}; -use crate::{der, io, rand, sealed}; +use crate::{der, rand, sealed}; use signing::RsaEncoding; /// An RSA key size. +#[non_exhaustive] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum KeySize { /// A 2048-bit modulus. @@ -187,6 +190,7 @@ fn spki(pkcs1: &[u8]) -> Result, Unspecified> { pub struct PublicKey { der: Box<[u8]>, n: Box<[u8]>, + #[cfg_attr(not(feature = "ring-io"), allow(dead_code))] e: Box<[u8]>, } @@ -210,12 +214,14 @@ impl PublicKey { } /// The modulus. + #[cfg(feature = "ring-io")] #[must_use] pub fn modulus(&self) -> io::Positive<'_> { io::Positive::new(&self.n) } /// The public exponent. + #[cfg(feature = "ring-io")] #[must_use] pub fn exponent(&self) -> io::Positive<'_> { io::Positive::new(&self.e) @@ -313,6 +319,7 @@ impl> PublicKeyComponents { } } +#[cfg(feature = "ring-io")] impl From<&PublicKey> for PublicKeyComponents> { fn from(public_key: &PublicKey) -> Self { Self { @@ -322,14 +329,17 @@ impl From<&PublicKey> for PublicKeyComponents> { } } -impl> TryFrom> for PublicEncryptingKey { +// aws-lc-rs implements `TryInto` rather than `TryFrom`, so +// `PublicEncryptingKey::try_from(components)` does not build there either. +#[allow(clippy::from_over_into)] +impl> TryInto for PublicKeyComponents { type Error = Unspecified; - fn try_from(components: PublicKeyComponents) -> Result { - let components = components.rind()?; + fn try_into(self) -> Result { + let components = self.rind()?; let key = rind::rsa::EncryptingKey::from_components(&rind::rsa::OAEP_SHA256, &components) .map_err(unspecified)?; - Ok(Self::new(key.as_ref())?) + Ok(PublicEncryptingKey::new(key.as_ref())?) } } diff --git a/rind-aws-lc-rs/tests/differential.rs b/rind-aws-lc-rs/tests/differential.rs index 46d990523..7fcfacc64 100644 --- a/rind-aws-lc-rs/tests/differential.rs +++ b/rind-aws-lc-rs/tests/differential.rs @@ -271,7 +271,26 @@ proptest! { .ok() .map(|plaintext| plaintext.to_vec()) }; - (open(&theirs_sealed), open(&corrupted)) + // The tag apart, at its full length or one byte short. + let open_separate = |sealed: &[u8], tag_len: usize| { + let (ciphertext, tag) = sealed.split_at(sealed.len() - 16); + let mut in_out = ciphertext.to_vec(); + key.open_in_place_separate_tag( + c::aead::Nonce::assume_unique_for_key(nonce), + c::aead::Aad::from(&aad), + &tag[..tag_len], + &mut in_out, + ) + .ok() + .map(|plaintext| plaintext.to_vec()) + }; + ( + open(&theirs_sealed), + open(&corrupted), + open_separate(&theirs_sealed, 16), + open_separate(&corrupted, 16), + open_separate(&theirs_sealed, 15), + ) }); } diff --git a/rind-aws-lc-rs/tests/keys.rs b/rind-aws-lc-rs/tests/keys.rs index 8a668d0ea..33d74a03c 100644 --- a/rind-aws-lc-rs/tests/keys.rs +++ b/rind-aws-lc-rs/tests/keys.rs @@ -244,6 +244,10 @@ proptest! { .decapsulate(theirs::kem::Ciphertext::from(ciphertext.as_ref())) .unwrap(); prop_assert_eq!(theirs_secret.as_ref(), secret.as_ref()); + + use ours::kem::AlgorithmIdentifier as _; + use theirs::kem::AlgorithmIdentifier as _; + prop_assert_eq!(ours::kem::ML_KEM_768.id().nid(), theirs::kem::ML_KEM_768.id().nid()); } } @@ -277,6 +281,7 @@ proptest! { let theirs_pair = theirs::rsa::KeyPair::from_pkcs8(rsa_pkcs8()).unwrap(); prop_assert_eq!(ours_pair.public_key().as_ref(), theirs_pair.public_key().as_ref()); prop_assert_eq!(ours_pair.public_modulus_len(), theirs_pair.public_modulus_len()); + #[cfg(feature = "ring-io")] prop_assert_eq!( ours_pair.public_key().modulus().big_endian_without_leading_zero(), theirs_pair.public_key().modulus().big_endian_without_leading_zero() @@ -325,8 +330,11 @@ proptest! { prop_assert!(key.verify(&msg, &sig).is_ok()); prop_assert!(key.verify(b"other", &sig).is_err()); } - let components: ours::rsa::PublicKeyComponents> = ours_pair.public_key().into(); - prop_assert!(components.verify(ours_params, &msg, &sig).is_ok()); + #[cfg(feature = "ring-io")] + { + let components: ours::rsa::PublicKeyComponents> = ours_pair.public_key().into(); + prop_assert!(components.verify(ours_params, &msg, &sig).is_ok()); + } } }