Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion MIGRATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
15 changes: 7 additions & 8 deletions MIGRATING.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -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

Expand All @@ -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
Expand Down
20 changes: 0 additions & 20 deletions quest/m0/adapters/aws-lc-rs.md

This file was deleted.

7 changes: 4 additions & 3 deletions rind-aws-lc-rs/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
Expand All @@ -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"]
Expand Down
4 changes: 2 additions & 2 deletions rind-aws-lc-rs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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.
Expand Down
10 changes: 5 additions & 5 deletions rind-aws-lc-rs/src/aead.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<A>,
tag: Tag,
tag: &[u8],
in_out: &'in_out mut [u8],
ciphertext: RangeFrom<usize>,
) -> 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>(
Expand Down
1 change: 1 addition & 0 deletions rind-aws-lc-rs/src/cipher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
23 changes: 0 additions & 23 deletions rind-aws-lc-rs/src/cpu.rs

This file was deleted.

14 changes: 7 additions & 7 deletions rind-aws-lc-rs/src/der.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -13,26 +13,26 @@ fn read<'a>(
input: &mut untrusted::Reader<'a>,
tag: u8,
) -> Result<untrusted::Input<'a>, 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),
}
}
_ => 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
Expand Down
25 changes: 23 additions & 2 deletions rind-aws-lc-rs/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,26 @@ 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<core::array::TryFromSliceError> for Unspecified {
fn from(_: core::array::TryFromSliceError) -> Self {
Self
}
}

#[cfg(any(feature = "ring-sig-verify", feature = "ring-io"))]
impl From<untrusted::EndOfInput> for Unspecified {
fn from(_: untrusted::EndOfInput) -> Self {
Self
Expand Down Expand Up @@ -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 {
Expand Down
43 changes: 26 additions & 17 deletions rind-aws-lc-rs/src/kem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ pub const ML_KEM_768: Algorithm<AlgorithmId> = 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.
Expand Down Expand Up @@ -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<Id: AlgorithmIdentifier> Algorithm<Id> {
/// 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}"),
}
}
}
Expand All @@ -79,18 +93,18 @@ where
key: rind::kem::DecapsulationKey,
}

impl DecapsulationKey<AlgorithmId> {
impl<Id: AlgorithmIdentifier> DecapsulationKey<Id> {
/// Generates a decapsulation key for `alg`.
pub fn generate(alg: &'static Algorithm<AlgorithmId>) -> Result<Self, Unspecified> {
let key = rind::kem::DecapsulationKey::generate(alg.id.rind()).map_err(unspecified)?;
pub fn generate(alg: &'static Algorithm<Id>) -> Result<Self, Unspecified> {
let key = rind::kem::DecapsulationKey::generate(alg.rind()).map_err(unspecified)?;
Ok(Self {
algorithm: alg,
key,
})
}

/// The key's encapsulation key.
pub fn encapsulation_key(&self) -> Result<EncapsulationKey<AlgorithmId>, Unspecified> {
pub fn encapsulation_key(&self) -> Result<EncapsulationKey<Id>, Unspecified> {
Ok(EncapsulationKey {
algorithm: self.algorithm,
key: self.key.public_key(),
Expand All @@ -108,9 +122,7 @@ impl DecapsulationKey<AlgorithmId> {
.map_err(unspecified)?;
Ok(SharedSecret(Wiped::new(secret.as_ref().into())))
}
}

impl<Id: AlgorithmIdentifier> DecapsulationKey<Id> {
/// The key's algorithm.
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm<Id> {
Expand All @@ -135,16 +147,15 @@ where
key: rind::kem::EncapsulationKey,
}

impl EncapsulationKey<AlgorithmId> {
impl<Id: AlgorithmIdentifier> EncapsulationKey<Id> {
/// Parses an encapsulation key for `alg` from its bytes.
pub fn new(alg: &'static Algorithm<AlgorithmId>, bytes: &[u8]) -> Result<Self, KeyRejected> {
pub fn new(alg: &'static Algorithm<Id>, bytes: &[u8]) -> Result<Self, KeyRejected> {
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,
Expand All @@ -165,9 +176,7 @@ impl EncapsulationKey<AlgorithmId> {
pub fn key_bytes(&self) -> Result<EncapsulationKeyBytes<'static>, Unspecified> {
Ok(EncapsulationKeyBytes::new(self.key.as_ref().to_vec()))
}
}

impl<Id: AlgorithmIdentifier> EncapsulationKey<Id> {
/// The key's algorithm.
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm<Id> {
Expand Down
Loading