Project import generated by Copybara. GitOrigin-RevId: 06b0c815d958bef0a60978d0766d65af839e37b6 Change-Id: Ifac0c45ae509ca8a8ed793fac5c981d4e23d3bf9
diff --git a/nearby/crypto/bssl-crypto/Cargo.toml b/nearby/crypto/bssl-crypto/Cargo.toml deleted file mode 100644 index bfe3964..0000000 --- a/nearby/crypto/bssl-crypto/Cargo.toml +++ /dev/null
@@ -1,9 +0,0 @@ -[package] -name = "bssl-crypto" -version.workspace = true -edition.workspace = true -publish.workspace = true - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies]
diff --git a/nearby/crypto/crypto_provider/Cargo.toml b/nearby/crypto/crypto_provider/Cargo.toml index 60f8626..e7dd8ba 100644 --- a/nearby/crypto/crypto_provider/Cargo.toml +++ b/nearby/crypto/crypto_provider/Cargo.toml
@@ -4,11 +4,14 @@ edition.workspace = true publish.workspace = true +[dependencies] +tinyvec.workspace = true + [dev-dependencies] criterion.workspace = true hex-literal.workspace = true crypto_provider_openssl.workspace = true -crypto_provider_rustcrypto.workspace = true +crypto_provider_rustcrypto = { workspace = true, features = ["std"] } rand_ext.workspace = true rand.workspace = true @@ -17,6 +20,7 @@ std = [] alloc = [] test_vectors = [] +raw_private_key_permit = [] [[bench]] name = "hmac_bench"
diff --git a/nearby/crypto/crypto_provider/src/aead.rs b/nearby/crypto/crypto_provider/src/aead.rs new file mode 100644 index 0000000..165272e --- /dev/null +++ b/nearby/crypto/crypto_provider/src/aead.rs
@@ -0,0 +1,86 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +/// An implementation of AES-GCM-SIV. +/// +/// An AesGcmSiv impl may be used for encryption and decryption. +pub trait AesGcmSiv: Aead<Nonce = [u8; 12]> {} + +/// An implementation of AES-GCM. +/// +/// An AesGcm impl may be used for encryption and decryption. +pub trait AesGcm: Aead<Nonce = [u8; 12]> {} + +/// Error returned on unsuccessful AEAD operation. +#[derive(Debug)] +pub struct AeadError; + +/// Initializes an AEAD +pub trait AeadInit<K: crate::aes::AesKey> { + /// Instantiates a new instance of the AEAD from key material. + fn new(key: &K) -> Self; +} + +/// Authenticated Encryption with Associated Data (AEAD) algorithm, where `N` is the size of the +/// Nonce. Encrypts and decrypts buffers in-place. +pub trait Aead { + /// The size of the authentication tag, this is appended to the message on the encrypt operation + /// and truncated from the plaintext after decrypting. + const TAG_SIZE: usize; + + /// The cryptographic nonce used by the AEAD. The nonce must be unique for all messages with + /// the same key. This is critically important - nonce reuse may completely undermine the + /// security of the AEAD. Nonces may be predictable and public, so long as they are unique. + type Nonce: AsRef<[u8]>; + + /// The type of the tag, which should always be [u8; Self::TAG_SIZE]. + type Tag: AsRef<[u8]>; + + /// Encrypt the given buffer containing a plaintext message. On success returns the encrypted + /// `msg` and appended auth tag, which will result in a Vec which is `Self::TAG_SIZE` bytes + /// greater than the initial message. + #[cfg(feature = "alloc")] + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>; + + /// Encrypt the given buffer containing a plaintext message in-place, and returns the tag in the + /// result value. + fn encrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + ) -> Result<Self::Tag, AeadError>; + + /// Decrypt the message, returning the decrypted plaintext or an error in the event the + /// provided authentication tag does not match the given ciphertext. On success the returned + /// Vec will only contain the plaintext and so will be `Self::TAG_SIZE` bytes less than the + /// initial message. + #[cfg(feature = "alloc")] + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError>; + + /// Decrypt the message in-place, returning an error and leaving the input `msg` unchanged in + /// the event the provided authentication tag does not match the given ciphertext. + fn decrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + tag: &Self::Tag, + ) -> Result<(), AeadError>; +}
diff --git a/nearby/crypto/crypto_provider/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider/src/aead/aes_gcm_siv.rs deleted file mode 100644 index 3be7db3..0000000 --- a/nearby/crypto/crypto_provider/src/aead/aes_gcm_siv.rs +++ /dev/null
@@ -1,23 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Traits for AES-GCM-SIV. - -extern crate alloc; -use crate::aead::Aead; - -/// An implementation of AES-GCM-SIV. -/// -/// An AesGcmSiv impl may be used for encryption and decryption. -pub trait AesGcmSiv: Aead<Nonce = [u8; 12]> {}
diff --git a/nearby/crypto/crypto_provider/src/aead/mod.rs b/nearby/crypto/crypto_provider/src/aead/mod.rs deleted file mode 100644 index 27284a9..0000000 --- a/nearby/crypto/crypto_provider/src/aead/mod.rs +++ /dev/null
@@ -1,50 +0,0 @@ -// Copyright 2023 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate alloc; -use alloc::vec::Vec; - -/// Contains traits for the AES-GCM-SIV AEAD algorithm. -pub mod aes_gcm_siv; - -/// Error returned on unsuccessful AEAD operation. -pub struct AeadError; - -/// Authenticated Encryption with Associated Data (AEAD) algorithm, where `N` is the size of the -/// Nonce. Encrypts and decrypts buffers in-place. -pub trait Aead { - /// The size of the authentication tag, this is appended to the message on the encrypt operation - /// and truncated from the plaintext after decrypting. - const TAG_SIZE: usize; - - /// The cryptographic nonce used by the AEAD. The nonce must be unique for all messages with - /// the same key. This is critically important - nonce reuse may completely undermine the - /// security of the AEAD. Nonces may be predictable and public, so long as they are unique. - type Nonce; - - /// The key material used to initialize the AEAD. - type Key; - - /// Instantiates a new instance of the AEAD from key material. - fn new(key: &Self::Key) -> Self; - - /// Encrypt the given buffer containing a plaintext message in-place. On success increases the - /// buffer by `Self::TAG_SIZE` bytes and appends the auth tag to the end of `msg`. - fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &Self::Nonce) -> Result<(), AeadError>; - - /// Decrypt the message in-place, returning an error in the event the provided authentication - /// tag does not match the given ciphertext. The buffer will be truncated to the length of the - /// original plaintext message upon success. - fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &Self::Nonce) -> Result<(), AeadError>; -}
diff --git a/nearby/crypto/crypto_provider/src/aes/cbc.rs b/nearby/crypto/crypto_provider/src/aes/cbc.rs index e32c588..59ce52d 100644 --- a/nearby/crypto/crypto_provider/src/aes/cbc.rs +++ b/nearby/crypto/crypto_provider/src/aes/cbc.rs
@@ -14,7 +14,10 @@ //! Traits for AES-CBC 256 with PKCS7 padding. +#[cfg(feature = "alloc")] extern crate alloc; +use crate::tinyvec::SliceVec; +#[cfg(feature = "alloc")] use alloc::vec::Vec; use super::Aes256Key; @@ -24,15 +27,53 @@ /// Trait for implementing AES-CBC with PKCS7 padding. pub trait AesCbcPkcs7Padded { + /// Calculate the padded output length (e.g. output of `encrypt`) from the unpadded length (e.g. + /// input message of `encrypt`). + fn padded_output_len(unpadded_len: usize) -> usize { + (unpadded_len - (unpadded_len % 16)) + .checked_add(16) + .expect("Padded output length is larger than usize::MAX") + } + /// Encrypt message using `key` and `iv`, returning a ciphertext. + #[cfg(feature = "alloc")] fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8>; + + /// Encrypt message using `key` and `iv` in-place in the given `message` vec. The given slice + /// vec should have enough capacity to contain both the ciphertext and the padding (which can be + /// calculated from `padded_output_len()`). If it doesn't have enough capacity, an error will be + /// returned. The contents of the input `message` buffer is undefined in that case. + fn encrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + message: &mut SliceVec<u8>, + ) -> Result<(), EncryptionError>; + /// Decrypt ciphertext using `key` and `iv`, returning the original message if `Ok()` otherwise /// a `DecryptionError` indicating the type of error that occurred while decrypting. + #[cfg(feature = "alloc")] fn decrypt( key: &Aes256Key, iv: &AesCbcIv, ciphertext: &[u8], ) -> Result<Vec<u8>, DecryptionError>; + + /// Decrypt ciphertext using `key` and `iv` and unpad it in-place. Returning the original + /// message if `Ok()` otherwise a `DecryptionError` indicating the type of error that occurred + /// while decrypting. In that case, the contents of the `ciphertext` buffer is undefined. + fn decrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + ciphertext: &mut SliceVec<u8>, + ) -> Result<(), DecryptionError>; +} + +/// Error type for describing what went wrong encrypting a message. +#[derive(Debug, PartialEq, Eq)] +pub enum EncryptionError { + /// Failed to add PKCS7 padding to the output when encrypting a message. This typically happens + /// when the given output buffer does not have enough capacity to append the padding. + PaddingFailed, } /// Error type for describing what went wrong decrypting a ciphertext. @@ -43,3 +84,65 @@ /// correctly. Exposing padding errors can cause a padding oracle vulnerability. BadPadding, } + +#[cfg(test)] +mod test { + #[cfg(feature = "alloc")] + extern crate alloc; + #[cfg(feature = "alloc")] + use alloc::vec::Vec; + + use crate::aes::Aes256Key; + use crate::tinyvec::SliceVec; + + use super::{AesCbcIv, AesCbcPkcs7Padded, DecryptionError, EncryptionError}; + + #[test] + fn test_padded_output_len() { + assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(0), 16); + assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(15), 16); + assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(16), 32); + assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(30), 32); + assert_eq!(AesCbcPkcs7PaddedStub::padded_output_len(32), 48); + } + + #[test] + #[should_panic] + fn test_padded_output_len_overflow() { + AesCbcPkcs7PaddedStub::padded_output_len(usize::MAX); + } + + struct AesCbcPkcs7PaddedStub; + + impl AesCbcPkcs7Padded for AesCbcPkcs7PaddedStub { + #[cfg(feature = "alloc")] + fn encrypt(_key: &Aes256Key, _iv: &AesCbcIv, _message: &[u8]) -> Vec<u8> { + unimplemented!() + } + + fn encrypt_in_place( + _key: &Aes256Key, + _iv: &AesCbcIv, + _message: &mut SliceVec<u8>, + ) -> Result<(), EncryptionError> { + unimplemented!() + } + + #[cfg(feature = "alloc")] + fn decrypt( + _key: &Aes256Key, + _iv: &AesCbcIv, + _ciphertext: &[u8], + ) -> Result<Vec<u8>, DecryptionError> { + unimplemented!() + } + + fn decrypt_in_place( + _key: &Aes256Key, + _iv: &AesCbcIv, + _ciphertext: &mut SliceVec<u8>, + ) -> Result<(), DecryptionError> { + unimplemented!() + } + } +}
diff --git a/nearby/crypto/crypto_provider/src/aes/ctr.rs b/nearby/crypto/crypto_provider/src/aes/ctr.rs index 73d4f55..6779fe8 100644 --- a/nearby/crypto/crypto_provider/src/aes/ctr.rs +++ b/nearby/crypto/crypto_provider/src/aes/ctr.rs
@@ -36,10 +36,8 @@ /// Build a `Self` from key material. fn new(key: &Self::Key, nonce_and_counter: NonceAndCounter) -> Self; - /// Encrypt the data in place, advancing the counter state appropriately. - fn encrypt(&mut self, data: &mut [u8]); - /// Decrypt the data in place, advancing the counter state appropriately. - fn decrypt(&mut self, data: &mut [u8]); + /// Applies the key stream to the data in place, advancing the counter state appropriately. + fn apply_keystream(&mut self, data: &mut [u8]); } /// The combined nonce and counter that CTR increments and encrypts to form the keystream.
diff --git a/nearby/crypto/crypto_provider/src/aes/mod.rs b/nearby/crypto/crypto_provider/src/aes/mod.rs index 83e48de..e359bad 100644 --- a/nearby/crypto/crypto_provider/src/aes/mod.rs +++ b/nearby/crypto/crypto_provider/src/aes/mod.rs
@@ -20,7 +20,6 @@ pub mod ctr; -#[cfg(feature = "alloc")] pub mod cbc; /// Block size in bytes for AES (and XTS-AES)
diff --git a/nearby/crypto/crypto_provider/src/ed25519.rs b/nearby/crypto/crypto_provider/src/ed25519.rs index 48c2c50..b435ca2 100644 --- a/nearby/crypto/crypto_provider/src/ed25519.rs +++ b/nearby/crypto/crypto_provider/src/ed25519.rs
@@ -43,6 +43,59 @@ /// A byte buffer the size of a ed25519 `PrivateKey`. pub type RawPrivateKey = [u8; PRIVATE_KEY_LENGTH]; +/// A permission token which may be supplied to methods which allow +/// converting private keys to/from raw bytes. +/// +/// In general, operations of this kind should only be done in +/// development-tools, tests, or in credential storage layers +/// to prevent accidental exposure of the private key. +pub struct RawPrivateKeyPermit { + _marker: (), +} + +impl RawPrivateKeyPermit { + pub(crate) fn new() -> Self { + Self { _marker: () } + } +} + +#[cfg(feature = "raw_private_key_permit")] +impl core::default::Default for RawPrivateKeyPermit { + fn default() -> Self { + Self::new() + } +} + +/// A crypto-provider-independent representation of the private +/// key of an ed25519 key-pair, kept in such a way that +/// it does not permit de-structuring it into raw bytes, +/// nor constructing one from raw bytes. +/// +/// Useful for when you want a data-structure to be +/// crypto-provider independent and contain a private key. +#[derive(Clone)] +pub struct PrivateKey { + wrapped: RawPrivateKey, +} + +impl PrivateKey { + /// Derives the public key corresponding to this private key. + pub fn derive_public_key<E: Ed25519Provider>(&self) -> E::PublicKey { + let key_pair = E::KeyPair::from_private_key(self); + key_pair.public() + } + /// Returns the raw bytes of this private key. + /// This operation is only possible while holding a [`RawPrivateKeyPermit`]. + pub fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey { + self.wrapped + } + /// Constructs a private key from the raw bytes of the key. + /// This operation is only possible while holding a [`RawPrivateKeyPermit`]. + pub fn from_raw_private_key(wrapped: RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self { + Self { wrapped } + } +} + /// The keypair which includes both public and secret halves of an asymmetric key. pub trait KeyPair: Sized { /// The ed25519 public key, used when verifying a message @@ -52,15 +105,37 @@ type Signature: Signature; /// Returns the private key bytes of the `KeyPair`. - /// This method should only ever be called by code which securely stores private credentials. - fn private_key(&self) -> RawPrivateKey; + /// This operation is only possible while holding a [`RawPrivateKeyPermit`]. + fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey; /// Builds a key-pair from a `RawPrivateKey` array of bytes. - /// This should only ever be called by code which securely stores private credentials. - fn from_private_key(bytes: &RawPrivateKey) -> Self + /// This operation is only possible while holding a [`RawPrivateKeyPermit`]. + fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self where Self: Sized; + /// Returns the private key of the `KeyPair` in an opaque form. + fn private_key(&self) -> PrivateKey { + // We're okay to reach in and grab the bytes of the private key, + // since the way that we're exposing it would require a valid + // [`RawPrivateKeyPermit`] to extract them again. + let wrapped = self.raw_private_key(&RawPrivateKeyPermit::new()); + PrivateKey { wrapped } + } + + /// Builds a key-pair from a [`PrivateKey`], given in an opaque form. + fn from_private_key(private_key: &PrivateKey) -> Self + where + Self: Sized, + { + // We're okay to reach in and construct an instance from + // the bytes of the private key, since the way that they + // were originally expressed would still require a valid + // [`RawPrivateKeyPermit`] to access them. + let raw_private_key = &private_key.wrapped; + Self::from_raw_private_key(raw_private_key, &RawPrivateKeyPermit::new()) + } + /// Sign the given message and return a digital signature fn sign(&self, msg: &[u8]) -> Self::Signature;
diff --git a/nearby/crypto/crypto_provider/src/elliptic_curve.rs b/nearby/crypto/crypto_provider/src/elliptic_curve.rs index d06a21d..d176769 100644 --- a/nearby/crypto/crypto_provider/src/elliptic_curve.rs +++ b/nearby/crypto/crypto_provider/src/elliptic_curve.rs
@@ -12,12 +12,8 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate alloc; - use core::fmt::Debug; -use alloc::vec::Vec; - /// Marker trait for an elliptic curve used for diffie-hellman. pub trait Curve {} @@ -42,12 +38,16 @@ /// The random number generator to be used for generating a secret type Rng: crate::CryptoRng; + /// The for encoded public key bytes, for example a `[u8; N]` array if the size is fixed, or + /// `ArrayVec<[u8; N]>` if the size is bounded but not fixed. + type EncodedPublicKey: AsRef<[u8]> + Debug; + /// Generates a new random ephemeral secret. fn generate_random(rng: &mut Self::Rng) -> Self; /// Returns the bytes of the public key for this ephemeral secret that is suitable for sending /// over the wire for key exchange. - fn public_key_bytes(&self) -> Vec<u8>; + fn public_key_bytes(&self) -> Self::EncodedPublicKey; /// Performs diffie-hellman key exchange using this ephemeral secret with the given public key /// `other_pub`. @@ -59,6 +59,8 @@ /// Trait for a public key used for elliptic curve diffie hellman. pub trait PublicKey<E: Curve>: Sized + PartialEq + Debug { + /// The type for an encoded public key. + type EncodedPublicKey: AsRef<[u8]> + Debug; /// The error type associated with Public Key. type Error: Debug; @@ -71,5 +73,5 @@ /// the sec1 encoding, may return equivalent but different byte-representations due to point /// compression, so it is not necessarily true that `from_bytes(bytes)?.to_bytes() == bytes` /// (but it is always true that `from_bytes(key.to_bytes())? == key`). - fn to_bytes(&self) -> Vec<u8>; + fn to_bytes(&self) -> Self::EncodedPublicKey; }
diff --git a/nearby/crypto/crypto_provider/src/lib.rs b/nearby/crypto/crypto_provider/src/lib.rs index 624072a..0031ebb 100644 --- a/nearby/crypto/crypto_provider/src/lib.rs +++ b/nearby/crypto/crypto_provider/src/lib.rs
@@ -48,6 +48,8 @@ /// mod containing traits for ed25519 key generation, signing, and verification pub mod ed25519; +pub use tinyvec; + /// Uber crypto trait which defines the traits for all crypto primitives as associated types pub trait CryptoProvider: Clone + Debug + PartialEq + Eq + Send { /// The Hkdf type which implements the hkdf trait @@ -80,10 +82,13 @@ /// using SHA-512 (SHA-2) and Curve25519 type Ed25519: ed25519::Ed25519Provider; /// The trait defining AES-128-GCM-SIV, a nonce-misuse resistant AEAD with a key size of 16 bytes. - type Aes128GcmSiv: aead::aes_gcm_siv::AesGcmSiv<Key = Aes128Key>; + type Aes128GcmSiv: aead::AesGcmSiv + aead::AeadInit<Aes128Key>; /// The trait defining AES-256-GCM-SIV, a nonce-misuse resistant AEAD with a key size of 32 bytes. - type Aes256GcmSiv: aead::aes_gcm_siv::AesGcmSiv<Key = Aes256Key>; - + type Aes256GcmSiv: aead::AesGcmSiv + aead::AeadInit<Aes256Key>; + /// The trait defining AES-128-GCM, an AEAD with a key size of 16 bytes. + type Aes128Gcm: aead::AesGcm + aead::AeadInit<Aes128Key>; + /// The trait defining AES-256-GCM, an AEAD with a key size of 32 bytes. + type Aes256Gcm: aead::AesGcm + aead::AeadInit<Aes256Key>; /// The cryptographically secure random number generator type CryptoRng: CryptoRng;
diff --git a/nearby/crypto/crypto_provider/src/p256.rs b/nearby/crypto/crypto_provider/src/p256.rs index 90d3542..fd7b531 100644 --- a/nearby/crypto/crypto_provider/src/p256.rs +++ b/nearby/crypto/crypto_provider/src/p256.rs
@@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate alloc; +use tinyvec::ArrayVec; use crate::elliptic_curve::{Curve, EcdhProvider, PublicKey}; -use alloc::vec::Vec; use core::fmt::Debug; /// Marker type for P256 implementation. This is used by EcdhProvider as its type parameter. @@ -23,6 +22,24 @@ pub enum P256 {} impl Curve for P256 {} +/// Longest length for a sec-1 encoded P256 public key, which is the uncompressed format +/// `04 || X || Y` as defined in section 2.3.3 of the SECG SEC 1 ("Elliptic Curve Cryptography") +/// standard. +const P256_PUBLIC_KEY_MAX_LENGTH: usize = 65; + +/// Whether an elliptic curve point should be compressed or not. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum PointCompression { + /// The elliptic curve point should be compressed (`02 || X` or `03 || X`), + /// as defined in section 2.3.3 of the SECG SEC 1 ("Elliptic Curve + /// Cryptography"). + Compressed, + /// The elliptic curve point should be uncompressed (`04 || X || Y`), as + /// defined in section 2.3.3 of the SECG SEC 1 ("Elliptic Curve + /// Cryptography"). + Uncompressed, +} + /// Trait for a NIST-P256 public key. pub trait P256PublicKey: Sized + PartialEq + Debug { /// The error type associated with this implementation. @@ -36,7 +53,10 @@ /// ("Elliptic Curve Cryptography") standard. Note that it is not necessarily true that /// `from_sec1_bytes(bytes)?.to_sec1_bytes() == bytes` because of point compression. (But it is /// always true that `from_sec1_bytes(key.to_sec1_bytes())? == key`). - fn to_sec1_bytes(&self) -> Vec<u8>; + fn to_sec1_bytes( + &self, + point_compression: PointCompression, + ) -> ArrayVec<[u8; P256_PUBLIC_KEY_MAX_LENGTH]>; /// Converts this public key's x and y coordinates on the elliptic curve to big endian octet /// strings. @@ -48,13 +68,14 @@ impl<P: P256PublicKey> PublicKey<P256> for P { type Error = <Self as P256PublicKey>::Error; + type EncodedPublicKey = ArrayVec<[u8; P256_PUBLIC_KEY_MAX_LENGTH]>; fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { Self::from_sec1_bytes(bytes) } - fn to_bytes(&self) -> Vec<u8> { - Self::to_sec1_bytes(self) + fn to_bytes(&self) -> Self::EncodedPublicKey { + Self::to_sec1_bytes(self, PointCompression::Uncompressed) } }
diff --git a/nearby/crypto/crypto_provider_boringssl/.cargo/config.toml b/nearby/crypto/crypto_provider_boringssl/.cargo/config.toml deleted file mode 100644 index f5ab7fa..0000000 --- a/nearby/crypto/crypto_provider_boringssl/.cargo/config.toml +++ /dev/null
@@ -1,3 +0,0 @@ -paths = [ - "../../../boringssl-build/boringssl/rust/bssl-crypto", -] \ No newline at end of file
diff --git a/nearby/crypto/crypto_provider_boringssl/Cargo.lock b/nearby/crypto/crypto_provider_boringssl/Cargo.lock index 14402c1..7773e56 100644 --- a/nearby/crypto/crypto_provider_boringssl/Cargo.lock +++ b/nearby/crypto/crypto_provider_boringssl/Cargo.lock
@@ -4,13 +4,20 @@ [[package]] name = "base64" -version = "0.13.1" +version = "0.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "9ba43ea6f343b788c8764558649e08df62f86c6ef251fdaeb1ffd010a9ae50a2" [[package]] name = "bssl-crypto" version = "0.1.0" +dependencies = [ + "bssl-sys", +] + +[[package]] +name = "bssl-sys" +version = "0.1.0" [[package]] name = "cfg-if" @@ -21,6 +28,9 @@ [[package]] name = "crypto_provider" version = "0.1.0" +dependencies = [ + "tinyvec", +] [[package]] name = "crypto_provider_boringssl" @@ -28,18 +38,10 @@ dependencies = [ "bssl-crypto", "crypto_provider", - "crypto_provider_stubs", "crypto_provider_test", ] [[package]] -name = "crypto_provider_stubs" -version = "0.1.0" -dependencies = [ - "crypto_provider", -] - -[[package]] name = "crypto_provider_test" version = "0.1.0" dependencies = [ @@ -56,9 +58,9 @@ [[package]] name = "getrandom" -version = "0.2.9" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c85e1d9ab2eadba7e5040d4e09cbd6d072b76a557ad64e797c2cb9d4da21d7e4" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" dependencies = [ "cfg-if", "libc", @@ -79,24 +81,21 @@ [[package]] name = "itoa" -version = "1.0.6" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" +checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" [[package]] name = "libc" -version = "0.2.144" +version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b00cc1c228a6782d0f076e7b232802e0c5689d41bb5df366f2a6b6621cfdfe1" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "log" -version = "0.4.17" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" -dependencies = [ - "cfg-if", -] +checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" [[package]] name = "ppv-lite86" @@ -106,18 +105,18 @@ [[package]] name = "proc-macro2" -version = "1.0.56" +version = "1.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435" +checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.27" +version = "1.0.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f4f29d145265ec1c483c7c654450edde0bfe043d3938d6972630663356d9500" +checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" dependencies = [ "proc-macro2", ] @@ -197,14 +196,14 @@ [[package]] name = "rstest_reuse" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45f80dcc84beab3a327bbe161f77db25f336a1452428176787c8c79ac79d7073" +checksum = "88530b681abe67924d42cca181d070e3ac20e0740569441a9e35a7cedd2b34a4" dependencies = [ "quote", "rand", "rustc_version", - "syn 1.0.109", + "syn 2.0.29", ] [[package]] @@ -218,41 +217,41 @@ [[package]] name = "ryu" -version = "1.0.13" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" +checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" [[package]] name = "semver" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bebd363326d05ec3e2f532ab7660680f3b02130d780c299bca73469d521bc0ed" +checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.162" +version = "1.0.187" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71b2f6e1ab5c2b98c05f0f35b236b22e8df7ead6ffbf51d7808da7f8817e7ab6" +checksum = "30a7fe14252655bd1e578af19f5fa00fe02fd0013b100ca6b49fde31c41bae4c" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.162" +version = "1.0.187" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2a0814352fd64b58489904a44ea8d90cb1a91dcb6b4f5ebabc32c8318e93cb6" +checksum = "e46b2a6ca578b3f1d4501b12f78ed4692006d79d82a1a7c561c12dbc3d625eb8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.15", + "syn 2.0.29", ] [[package]] name = "serde_json" -version = "1.0.96" +version = "1.0.105" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057d394a50403bcac12672b2b18fb387ab6d289d957dab67dd201875391e52f1" +checksum = "693151e1ac27563d6dbcec9dee9fbd5da8539b20fa14ad3752b2e6d363ace360" dependencies = [ "itoa", "ryu", @@ -272,9 +271,9 @@ [[package]] name = "syn" -version = "2.0.15" +version = "2.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a34fcf3e8b60f57e6a14301a2e916d323af98b0ea63c599441eec8558660c822" +checksum = "c324c494eba9d92503e6f1ef2e6df781e78f6a7705a0202d9801b198807d518a" dependencies = [ "proc-macro2", "quote", @@ -290,10 +289,16 @@ ] [[package]] -name = "unicode-ident" -version = "1.0.8" +name = "tinyvec" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" + +[[package]] +name = "unicode-ident" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "wasi" @@ -303,9 +308,9 @@ [[package]] name = "wycheproof" -version = "0.4.0" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "183c789620c674b79dac33cd3aadb6c8006b66cba6a680402235aaebc743e3df" +checksum = "e639f57253b80c6584b378011aec0fed61c4c21d7a4b97c4d9d7eaf35ca77d12" dependencies = [ "base64", "hex",
diff --git a/nearby/crypto/crypto_provider_boringssl/Cargo.toml b/nearby/crypto/crypto_provider_boringssl/Cargo.toml index 24ce66c..fb3bfdf 100644 --- a/nearby/crypto/crypto_provider_boringssl/Cargo.toml +++ b/nearby/crypto/crypto_provider_boringssl/Cargo.toml
@@ -6,10 +6,9 @@ [dependencies] crypto_provider = { path = "../crypto_provider", features = ["alloc", "std"] } -crypto_provider_stubs = { path = "../crypto_provider_stubs" } -# Note: before this crate will work you need to run `scripts/prepare-boringssl.sh` -bssl-crypto = {path = "../bssl-crypto"} +# Note: before this crate will work you need to run `cargo run -p build_scripts -- build-boringssl` +bssl-crypto = {path = "../../../third_party/boringssl/rust/bssl-crypto"} [dev-dependencies] crypto_provider_test = {path = "../crypto_provider_test"}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs new file mode 100644 index 0000000..03bb087 --- /dev/null +++ b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm.rs
@@ -0,0 +1,89 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate alloc; + +use alloc::vec::Vec; +use bssl_crypto::aead::Aead as _; +use crypto_provider::aead::{Aead, AeadError, AeadInit}; +use crypto_provider::aes::{Aes128Key, Aes256Key, AesKey}; + +pub struct AesGcm(bssl_crypto::aead::AesGcm); + +impl AeadInit<Aes128Key> for AesGcm { + fn new(key: &Aes128Key) -> Self { + Self(bssl_crypto::aead::new_aes_128_gcm(key.as_array())) + } +} + +impl AeadInit<Aes256Key> for AesGcm { + fn new(key: &Aes256Key) -> Self { + Self(bssl_crypto::aead::new_aes_256_gcm(key.as_array())) + } +} + +impl crypto_provider::aead::AesGcm for AesGcm {} + +impl Aead for AesGcm { + const TAG_SIZE: usize = 16; + type Nonce = [u8; 12]; + type Tag = [u8; 16]; + + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0.encrypt(msg, aad, nonce).map_err(|_| AeadError) + } + + fn encrypt_detached( + &self, + _msg: &mut [u8], + _aad: &[u8], + _nonce: &Self::Nonce, + ) -> Result<Self::Tag, AeadError> { + unimplemented!("Not yet supported by boringssl") + } + + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0.decrypt(msg, aad, nonce).map_err(|_| AeadError) + } + + fn decrypt_detached( + &self, + _msg: &mut [u8], + _aad: &[u8], + _nonce: &Self::Nonce, + _tag: &Self::Tag, + ) -> Result<(), AeadError> { + unimplemented!("Not yet supported by boringssl") + } +} + +#[cfg(test)] +mod tests { + use core::marker::PhantomData; + + use crypto_provider_test::aead::aes_gcm::*; + use crypto_provider_test::aes::*; + + use super::*; + + #[apply(aes_128_gcm_test_cases)] + fn aes_gcm_128_test(testcase: CryptoProviderTestCase<AesGcm>) { + testcase(PhantomData); + } + + #[apply(aes_256_gcm_test_cases)] + fn aes_gcm_256_test(testcase: CryptoProviderTestCase<AesGcm>) { + testcase(PhantomData); + } +}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm_siv.rs new file mode 100644 index 0000000..d1fb0e7 --- /dev/null +++ b/nearby/crypto/crypto_provider_boringssl/src/aead/aes_gcm_siv.rs
@@ -0,0 +1,88 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +extern crate alloc; + +use alloc::vec::Vec; +use bssl_crypto::aead::Aead as _; +use crypto_provider::aead::{Aead, AeadError, AeadInit}; +use crypto_provider::aes::{Aes128Key, Aes256Key, AesKey}; + +pub struct AesGcmSiv(bssl_crypto::aead::AesGcmSiv); + +impl AeadInit<Aes128Key> for AesGcmSiv { + fn new(key: &Aes128Key) -> Self { + Self(bssl_crypto::aead::new_aes_128_gcm_siv(key.as_array())) + } +} + +impl AeadInit<Aes256Key> for AesGcmSiv { + fn new(key: &Aes256Key) -> Self { + Self(bssl_crypto::aead::new_aes_256_gcm_siv(key.as_array())) + } +} + +impl crypto_provider::aead::AesGcmSiv for AesGcmSiv {} + +impl Aead for AesGcmSiv { + const TAG_SIZE: usize = 16; + type Nonce = [u8; 12]; + type Tag = [u8; 16]; + + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0.encrypt(msg, aad, nonce).map_err(|_| AeadError) + } + + fn encrypt_detached( + &self, + _msg: &mut [u8], + _aad: &[u8], + _nonce: &Self::Nonce, + ) -> Result<Self::Tag, AeadError> { + unimplemented!("Not yet supported by boringssl") + } + + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0.decrypt(msg, aad, nonce).map_err(|_| AeadError) + } + + fn decrypt_detached( + &self, + _msg: &mut [u8], + _aad: &[u8], + _nonce: &Self::Nonce, + _tag: &Self::Tag, + ) -> Result<(), AeadError> { + unimplemented!("Not yet supported by boringssl") + } +} + +#[cfg(test)] +mod tests { + use core::marker::PhantomData; + + use crypto_provider_test::aead::aes_gcm_siv::*; + use crypto_provider_test::aes::*; + + use super::*; + + #[apply(aes_128_gcm_siv_test_cases)] + fn aes_gcm_siv_128_test(testcase: CryptoProviderTestCase<AesGcmSiv>) { + testcase(PhantomData); + } + + #[apply(aes_256_gcm_siv_test_cases)] + fn aes_gcm_siv_256_test(testcase: CryptoProviderTestCase<AesGcmSiv>) { + testcase(PhantomData); + } +}
diff --git a/nearby/crypto/bssl-crypto/src/lib.rs b/nearby/crypto/crypto_provider_boringssl/src/aead/mod.rs similarity index 73% rename from nearby/crypto/bssl-crypto/src/lib.rs rename to nearby/crypto/crypto_provider_boringssl/src/aead/mod.rs index 89e6968..424f16e 100644 --- a/nearby/crypto/bssl-crypto/src/lib.rs +++ b/nearby/crypto/crypto_provider_boringssl/src/aead/mod.rs
@@ -4,7 +4,7 @@ // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, @@ -12,5 +12,5 @@ // See the License for the specific language governing permissions and // limitations under the License. -//! Placeholder crate to satisfy cargo. If actually using boring ssl, please run the -//! `build-boringssl` subcommand of the top level crate. +pub(crate) mod aes_gcm; +pub(crate) mod aes_gcm_siv;
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aes/cbc.rs b/nearby/crypto/crypto_provider_boringssl/src/aes/cbc.rs new file mode 100644 index 0000000..4d3fd3f --- /dev/null +++ b/nearby/crypto/crypto_provider_boringssl/src/aes/cbc.rs
@@ -0,0 +1,81 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate alloc; + +use alloc::vec::Vec; +use bssl_crypto::cipher::BlockCipher; +use crypto_provider::{ + aes::{ + cbc::{AesCbcIv, DecryptionError, EncryptionError}, + Aes256Key, AesKey, + }, + tinyvec::SliceVec, +}; + +/// BoringSSL implementation of AES-CBC with PKCS7 padding +pub enum AesCbcPkcs7Padded {} +impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for AesCbcPkcs7Padded { + #[allow(clippy::expect_used)] + fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> { + let encryptor = bssl_crypto::cipher::aes_cbc::Aes256Cbc::new_encrypt(key.as_array(), iv); + encryptor.encrypt_padded(message).expect("Encrypting AES-CBC should be infallible") + } + + fn encrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + message: &mut SliceVec<u8>, + ) -> Result<(), EncryptionError> { + let result = Self::encrypt(key, iv, message); + if message.capacity() < result.len() { + return Err(EncryptionError::PaddingFailed); + } + message.clear(); + message.extend_from_slice(&result); + Ok(()) + } + + fn decrypt( + key: &Aes256Key, + iv: &AesCbcIv, + ciphertext: &[u8], + ) -> Result<Vec<u8>, DecryptionError> { + let decryptor = bssl_crypto::cipher::aes_cbc::Aes256Cbc::new_decrypt(key.as_array(), iv); + decryptor.decrypt_padded(ciphertext).map_err(|_| DecryptionError::BadPadding) + } + + fn decrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + ciphertext: &mut SliceVec<u8>, + ) -> Result<(), DecryptionError> { + Self::decrypt(key, iv, ciphertext).map(|result| { + ciphertext.clear(); + ciphertext.extend_from_slice(&result); + }) + } +} + +#[cfg(test)] +mod tests { + use super::AesCbcPkcs7Padded; + use core::marker::PhantomData; + use crypto_provider_test::aes::cbc::*; + + #[apply(aes_256_cbc_test_cases)] + fn aes_256_cbc_test(testcase: CryptoProviderTestCase<AesCbcPkcs7Padded>) { + testcase(PhantomData); + } +}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aes/ctr.rs b/nearby/crypto/crypto_provider_boringssl/src/aes/ctr.rs new file mode 100644 index 0000000..c13ab31 --- /dev/null +++ b/nearby/crypto/crypto_provider_boringssl/src/aes/ctr.rs
@@ -0,0 +1,74 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use bssl_crypto::cipher::StreamCipher; +use crypto_provider::aes::{ctr::NonceAndCounter, Aes128Key, Aes256Key, AesKey}; + +/// BoringSSL implementation of AES-CTR 128. +pub struct AesCtr128(bssl_crypto::cipher::aes_ctr::Aes128Ctr); + +impl crypto_provider::aes::ctr::AesCtr for AesCtr128 { + type Key = Aes128Key; + + fn new(key: &Self::Key, nonce_and_counter: NonceAndCounter) -> Self { + Self(bssl_crypto::cipher::aes_ctr::Aes128Ctr::new( + key.as_array(), + &nonce_and_counter.as_block_array(), + )) + } + + #[allow(clippy::expect_used)] + fn apply_keystream(&mut self, data: &mut [u8]) { + assert!(data.len() < i32::MAX as usize); + self.0.apply_keystream(data).expect("Data length should fit inside of a i32") + } +} + +/// BoringSSL implementation of AES-CTR 256. +pub struct AesCtr256(bssl_crypto::cipher::aes_ctr::Aes256Ctr); + +impl crypto_provider::aes::ctr::AesCtr for AesCtr256 { + type Key = Aes256Key; + + fn new(key: &Self::Key, nonce_and_counter: NonceAndCounter) -> Self { + Self(bssl_crypto::cipher::aes_ctr::Aes256Ctr::new( + key.as_array(), + &nonce_and_counter.as_block_array(), + )) + } + + #[allow(clippy::expect_used)] + fn apply_keystream(&mut self, data: &mut [u8]) { + assert!(data.len() < i32::MAX as usize); + self.0.apply_keystream(data).expect("Data length should fit inside of a i32") + } +} + +#[cfg(test)] +mod tests { + use super::*; + use core::marker::PhantomData; + use crypto_provider_test::aes::ctr::*; + use crypto_provider_test::aes::*; + + #[apply(aes_128_ctr_test_cases)] + fn aes_128_ctr_test(testcase: CryptoProviderTestCase<AesCtr128>) { + testcase(PhantomData); + } + + #[apply(aes_256_ctr_test_cases)] + fn aes_256_ctr_test(testcase: CryptoProviderTestCase<AesCtr256>) { + testcase(PhantomData); + } +}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/aes.rs b/nearby/crypto/crypto_provider_boringssl/src/aes/mod.rs similarity index 97% rename from nearby/crypto/crypto_provider_boringssl/src/aes.rs rename to nearby/crypto/crypto_provider_boringssl/src/aes/mod.rs index 88bfeac..d5cbe58 100644 --- a/nearby/crypto/crypto_provider_boringssl/src/aes.rs +++ b/nearby/crypto/crypto_provider_boringssl/src/aes/mod.rs
@@ -17,6 +17,12 @@ Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, AesKey, }; +/// AES_CTR implementations. +pub mod ctr; + +/// AES_CBC implementations. +pub mod cbc; + /// BoringSSL AES-128 operations pub struct Aes128; impl Aes for Aes128 {
diff --git a/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs b/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs index 7c671ae..b5c374a 100644 --- a/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs +++ b/nearby/crypto/crypto_provider_boringssl/src/ed25519.rs
@@ -13,7 +13,8 @@ // limitations under the License. use crypto_provider::ed25519::{ - InvalidBytes, RawPrivateKey, RawPublicKey, RawSignature, Signature as _, SignatureError, + InvalidBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature, Signature as _, + SignatureError, }; pub struct Ed25519; @@ -30,11 +31,11 @@ type PublicKey = PublicKey; type Signature = Signature; - fn private_key(&self) -> RawPrivateKey { + fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey { self.0.to_seed() } - fn from_private_key(bytes: &RawPrivateKey) -> Self + fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self where Self: Sized, {
diff --git a/nearby/crypto/crypto_provider_boringssl/src/lib.rs b/nearby/crypto/crypto_provider_boringssl/src/lib.rs index 3b78898..75758b6 100644 --- a/nearby/crypto/crypto_provider_boringssl/src/lib.rs +++ b/nearby/crypto/crypto_provider_boringssl/src/lib.rs
@@ -21,14 +21,12 @@ clippy::expect_used )] -//! Crate which provides impls for CryptoProvider backed by BoringSSL. - -use bssl_crypto::digest::{Sha256, Sha512}; +//! Crate which provides impls for CryptoProvider backed by BoringSSL +//! use bssl_crypto::rand::rand_bytes; use crypto_provider::{CryptoProvider, CryptoRng}; -use crypto_provider_stubs::*; -/// Implementation of `crypto_provider::aes` types using BoringSSL. +/// Implementation of `crypto_provider::aes` types using BoringSSL pub mod aes; /// Implementations of crypto_provider::hkdf traits backed by BoringSSL @@ -40,31 +38,45 @@ /// Implementations of crypto_provider::ed25519 traits backed by BoringSSL mod ed25519; +/// Implementations of crypto_provider::aead traits backed by BoringSSL +mod aead; + +/// Implementations of crypto_provider::p256 traits backed by BoringSSL +mod p256; + +/// Implementations of crypto_provider::x25519 traits backed by BoringSSL +mod x25519; + +/// Implementations of crypto_provider::sha2 traits backed by BoringSSL +mod sha2; + /// The BoringSSL backed struct which implements CryptoProvider #[derive(Default, Clone, Debug, PartialEq, Eq)] pub struct Boringssl; impl CryptoProvider for Boringssl { - type HkdfSha256 = hkdf::Hkdf<Sha256>; + type HkdfSha256 = hkdf::Hkdf<bssl_crypto::digest::Sha256>; type HmacSha256 = hmac::HmacSha256; - type HkdfSha512 = hkdf::Hkdf<Sha512>; + type HkdfSha512 = hkdf::Hkdf<bssl_crypto::digest::Sha512>; type HmacSha512 = hmac::HmacSha512; - type AesCbcPkcs7Padded = AesCbcPkcs7PaddedStubs; - type X25519 = X25519Stubs; - type P256 = P256Stubs; - type Sha256 = Sha2Stubs; - type Sha512 = Sha2Stubs; + type AesCbcPkcs7Padded = aes::cbc::AesCbcPkcs7Padded; + type X25519 = x25519::X25519Ecdh; + type P256 = p256::P256Ecdh; + type Sha256 = sha2::Sha256; + type Sha512 = sha2::Sha512; type Aes128 = aes::Aes128; type Aes256 = aes::Aes256; - type AesCtr128 = Aes128Stubs; - type AesCtr256 = Aes256Stubs; + type AesCtr128 = aes::ctr::AesCtr128; + type AesCtr256 = aes::ctr::AesCtr256; type Ed25519 = ed25519::Ed25519; - type Aes128GcmSiv = Aes128Stubs; - type Aes256GcmSiv = Aes256Stubs; + type Aes128GcmSiv = aead::aes_gcm_siv::AesGcmSiv; + type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv; + type Aes128Gcm = aead::aes_gcm::AesGcm; + type Aes256Gcm = aead::aes_gcm::AesGcm; type CryptoRng = BoringSslRng; - fn constant_time_eq(_a: &[u8], _b: &[u8]) -> bool { - unimplemented!() + fn constant_time_eq(a: &[u8], b: &[u8]) -> bool { + bssl_crypto::mem::crypto_memcmp(a, b) } } @@ -86,3 +98,17 @@ rand_bytes(dest) } } + +#[cfg(test)] +mod tests { + use core::marker::PhantomData; + use crypto_provider_test::prelude::*; + use crypto_provider_test::sha2::*; + + use crate::Boringssl; + + #[apply(sha2_test_cases)] + fn sha2_tests(testcase: CryptoProviderTestCase<Boringssl>) { + testcase(PhantomData); + } +}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/p256.rs b/nearby/crypto/crypto_provider_boringssl/src/p256.rs new file mode 100644 index 0000000..7fefecd --- /dev/null +++ b/nearby/crypto/crypto_provider_boringssl/src/p256.rs
@@ -0,0 +1,117 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +extern crate alloc; + +use bssl_crypto::ecdh::{PrivateKey, PublicKey}; +use crypto_provider::{ + elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey as _}, + p256::{PointCompression, P256}, + tinyvec::ArrayVec, +}; + +/// Implementation of NIST-P256 using bssl-crypto crate. +pub struct P256Ecdh; + +impl EcdhProvider<P256> for P256Ecdh { + type PublicKey = P256PublicKey; + type EphemeralSecret = P256EphemeralSecret; + type SharedSecret = [u8; 32]; +} + +/// A NIST-P256 public key. +#[derive(Debug, PartialEq, Eq)] +pub struct P256PublicKey(PublicKey<bssl_crypto::ecdh::P256>); +impl crypto_provider::p256::P256PublicKey for P256PublicKey { + type Error = bssl_crypto::ecdh::Error; + + fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { + PublicKey::try_from(bytes).map(Self) + } + + fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> { + match point_compression { + PointCompression::Compressed => unimplemented!(), + PointCompression::Uncompressed => { + let mut bytes = ArrayVec::<[u8; 65]>::new(); + bytes.extend_from_slice(&self.0.to_vec()); + bytes + } + } + } + + #[allow(clippy::expect_used)] + fn to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error> { + Ok(self.0.to_affine_coordinates()) + } + fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> { + PublicKey::<bssl_crypto::ecdh::P256>::from_affine_coordinates(x, y).map(Self) + } +} + +/// Ephemeral secrect for use in a P256 Diffie-Hellman +pub struct P256EphemeralSecret { + secret: PrivateKey<bssl_crypto::ecdh::P256>, +} + +impl EphemeralSecret<P256> for P256EphemeralSecret { + type Impl = P256Ecdh; + type Error = bssl_crypto::ecdh::Error; + type Rng = (); + type EncodedPublicKey = + <P256PublicKey as crypto_provider::elliptic_curve::PublicKey<P256>>::EncodedPublicKey; + + fn generate_random(_rng: &mut Self::Rng) -> Self { + Self { secret: PrivateKey::generate() } + } + + fn public_key_bytes(&self) -> Self::EncodedPublicKey { + P256PublicKey((&self.secret).into()).to_bytes() + } + + fn diffie_hellman( + self, + other_pub: &P256PublicKey, + ) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error> { + let shared_secret = self.secret.diffie_hellman(&other_pub.0)?; + let bytes: <Self::Impl as EcdhProvider<P256>>::SharedSecret = shared_secret.to_bytes(); + Ok(bytes) + } +} + +#[cfg(test)] +impl crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<P256> for P256EphemeralSecret { + #[allow(clippy::unwrap_used)] + fn from_private_components( + private_bytes: &[u8; 32], + _public_key: &P256PublicKey, + ) -> Result<Self, Self::Error> { + Ok(Self { secret: PrivateKey::from_private_bytes(private_bytes).unwrap() }) + } +} + +#[cfg(test)] +mod tests { + use super::P256Ecdh; + use core::marker::PhantomData; + use crypto_provider_test::p256::*; + + #[apply(p256_test_cases)] + fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>, name: &str) { + if name == "to_bytes_compressed" { + return; // EC point compression not supported by bssl-crypto yet + } + testcase(PhantomData::<P256Ecdh>) + } +}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/sha2.rs b/nearby/crypto/crypto_provider_boringssl/src/sha2.rs new file mode 100644 index 0000000..d95294d --- /dev/null +++ b/nearby/crypto/crypto_provider_boringssl/src/sha2.rs
@@ -0,0 +1,35 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +/// BoringSSL implementation for SHA256 +pub struct Sha256; + +impl crypto_provider::sha2::Sha256 for Sha256 { + fn sha256(input: &[u8]) -> [u8; 32] { + let mut digest = bssl_crypto::digest::Sha256::new_digest(); + digest.update(input); + digest.finalize() + } +} + +/// BoringSSL implementation for SHA512 +pub struct Sha512; + +impl crypto_provider::sha2::Sha512 for Sha512 { + fn sha512(input: &[u8]) -> [u8; 64] { + let mut digest = bssl_crypto::digest::Sha512::new_digest(); + digest.update(input); + digest.finalize() + } +}
diff --git a/nearby/crypto/crypto_provider_boringssl/src/x25519.rs b/nearby/crypto/crypto_provider_boringssl/src/x25519.rs new file mode 100644 index 0000000..58d040b --- /dev/null +++ b/nearby/crypto/crypto_provider_boringssl/src/x25519.rs
@@ -0,0 +1,102 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey}; +use crypto_provider::x25519::X25519; + +/// The BoringSSL implementation of X25519 ECDH. +pub struct X25519Ecdh; + +impl EcdhProvider<X25519> for X25519Ecdh { + type PublicKey = X25519PublicKey; + type EphemeralSecret = X25519EphemeralSecret; + type SharedSecret = [u8; 32]; +} + +/// A X25519 ephemeral secret used for Diffie-Hellman. +pub struct X25519EphemeralSecret { + secret: bssl_crypto::x25519::PrivateKey, +} + +impl EphemeralSecret<X25519> for X25519EphemeralSecret { + type Impl = X25519Ecdh; + type Error = bssl_crypto::x25519::DiffieHellmanError; + type Rng = (); + type EncodedPublicKey = [u8; 32]; + + fn generate_random(_rng: &mut Self::Rng) -> Self { + Self { secret: bssl_crypto::x25519::PrivateKey::generate() } + } + + fn public_key_bytes(&self) -> Self::EncodedPublicKey { + let pubkey: bssl_crypto::x25519::PublicKey = (&self.secret).into(); + pubkey.to_bytes() + } + + fn diffie_hellman( + self, + other_pub: &<X25519Ecdh as EcdhProvider<X25519>>::PublicKey, + ) -> Result<<X25519Ecdh as EcdhProvider<X25519>>::SharedSecret, Self::Error> { + self.secret.diffie_hellman(&other_pub.0).map(|secret| secret.to_bytes()) + } +} + +#[cfg(test)] +impl crypto_provider_test::elliptic_curve::EphemeralSecretForTesting<X25519> + for X25519EphemeralSecret +{ + fn from_private_components( + private_bytes: &[u8; 32], + _public_key: &X25519PublicKey, + ) -> Result<Self, Self::Error> { + Ok(Self { secret: bssl_crypto::x25519::PrivateKey::from_private_bytes(private_bytes) }) + } +} + +/// A X25519 public key. +#[derive(Debug, PartialEq, Eq)] +pub struct X25519PublicKey(bssl_crypto::x25519::PublicKey); + +impl PublicKey<X25519> for X25519PublicKey { + type Error = Error; + type EncodedPublicKey = [u8; 32]; + + fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { + let byte_arr: [u8; 32] = bytes.try_into().map_err(|_| Error::WrongSize)?; + Ok(Self(bssl_crypto::x25519::PublicKey::from(&byte_arr))) + } + + fn to_bytes(&self) -> Self::EncodedPublicKey { + self.0.to_bytes() + } +} + +/// Error type for the BoringSSL implementation of x25519. +#[derive(Debug)] +pub enum Error { + /// Unexpected size for the given input. + WrongSize, +} + +#[cfg(test)] +mod tests { + use super::X25519Ecdh; + use core::marker::PhantomData; + use crypto_provider_test::x25519::*; + + #[apply(x25519_test_cases)] + fn x25519_tests(testcase: CryptoProviderTestCase<X25519Ecdh>) { + testcase(PhantomData::<X25519Ecdh>) + } +}
diff --git a/nearby/crypto/crypto_provider_default/src/lib.rs b/nearby/crypto/crypto_provider_default/src/lib.rs index 4b1b01e..b880ba8 100644 --- a/nearby/crypto/crypto_provider_default/src/lib.rs +++ b/nearby/crypto/crypto_provider_default/src/lib.rs
@@ -15,6 +15,10 @@ //! Provides multiple implementations of CryptoProvider through the same struct, configurable by //! feature flag. +#![no_std] +#![forbid(unsafe_code)] +#![deny(missing_docs)] + cfg_if::cfg_if! { if #[cfg(feature = "rustcrypto")] { pub use crypto_provider_rustcrypto::RustCrypto as CryptoProviderImpl;
diff --git a/nearby/crypto/crypto_provider_openssl/src/aes.rs b/nearby/crypto/crypto_provider_openssl/src/aes.rs index f5d5b00..c2ac5b1 100644 --- a/nearby/crypto/crypto_provider_openssl/src/aes.rs +++ b/nearby/crypto/crypto_provider_openssl/src/aes.rs
@@ -26,10 +26,13 @@ use openssl::symm::{Cipher, Crypter, Mode}; -use crypto_provider::aes::{ - cbc::{AesCbcIv, DecryptionError}, - ctr::NonceAndCounter, - Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, AesKey, +use crypto_provider::{ + aes::{ + cbc::{AesCbcIv, DecryptionError, EncryptionError}, + ctr::NonceAndCounter, + Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, AesKey, + }, + tinyvec::SliceVec, }; /// Uber struct which contains impls for AES-128 fns @@ -126,7 +129,23 @@ impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for OpenSslAesCbcPkcs7 { fn encrypt(key: &crypto_provider::aes::Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> { openssl::symm::encrypt(Cipher::aes_256_cbc(), key.as_slice(), Some(iv.as_slice()), message) - .unwrap() + // The output buffer is allocated by the openssl crate and guarantees to have enough + // space to hold the output value and does not overlap with the input slice. + .expect("encrypt should always succeed") + } + + fn encrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + message: &mut SliceVec<u8>, + ) -> Result<(), EncryptionError> { + let encrypted = Self::encrypt(key, iv, message); + if encrypted.len() > message.capacity() { + return Err(EncryptionError::PaddingFailed); + } + message.clear(); + message.extend_from_slice(&encrypted); + Ok(()) } fn decrypt( @@ -142,12 +161,22 @@ ) .map_err(|_| DecryptionError::BadPadding) } + + fn decrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + ciphertext: &mut SliceVec<u8>, + ) -> Result<(), DecryptionError> { + Self::decrypt(key, iv, ciphertext).map(|result| { + ciphertext.clear(); + ciphertext.extend_from_slice(&result); + }) + } } /// OpenSSL implementation of AES-CTR-128 pub struct OpenSslAesCtr128 { enc_cipher: Crypter, - dec_cipher: Crypter, } impl crypto_provider::aes::ctr::AesCtr for OpenSslAesCtr128 { @@ -161,33 +190,19 @@ Some(&nonce_and_counter.as_block_array()), ) .unwrap(), - dec_cipher: Crypter::new( - Cipher::aes_128_ctr(), - Mode::Decrypt, - key.as_slice(), - Some(&nonce_and_counter.as_block_array()), - ) - .unwrap(), } } - fn encrypt(&mut self, data: &mut [u8]) { + fn apply_keystream(&mut self, data: &mut [u8]) { let mut in_slice = vec![0u8; data.len()]; in_slice.copy_from_slice(data); let _ = self.enc_cipher.update(&in_slice, data); } - - fn decrypt(&mut self, data: &mut [u8]) { - let mut in_slice = vec![0u8; data.len()]; - in_slice.copy_from_slice(data); - let _ = self.dec_cipher.update(&in_slice, data); - } } /// OpenSSL implementation of AES-CTR-256 pub struct OpenSslAesCtr256 { enc_cipher: Crypter, - dec_cipher: Crypter, } impl crypto_provider::aes::ctr::AesCtr for OpenSslAesCtr256 { @@ -201,27 +216,14 @@ Some(&nonce_and_counter.as_block_array()), ) .unwrap(), - dec_cipher: Crypter::new( - Cipher::aes_256_ctr(), - Mode::Decrypt, - key.as_slice(), - Some(&nonce_and_counter.as_block_array()), - ) - .unwrap(), } } - fn encrypt(&mut self, data: &mut [u8]) { + fn apply_keystream(&mut self, data: &mut [u8]) { let mut in_slice = vec![0u8; data.len()]; in_slice.copy_from_slice(data); let _ = self.enc_cipher.update(&in_slice, data); } - - fn decrypt(&mut self, data: &mut [u8]) { - let mut in_slice = vec![0u8; data.len()]; - in_slice.copy_from_slice(data); - let _ = self.dec_cipher.update(&in_slice, data); - } } #[cfg(test)]
diff --git a/nearby/crypto/crypto_provider_openssl/src/ed25519.rs b/nearby/crypto/crypto_provider_openssl/src/ed25519.rs index 110daf2..111348a 100644 --- a/nearby/crypto/crypto_provider_openssl/src/ed25519.rs +++ b/nearby/crypto/crypto_provider_openssl/src/ed25519.rs
@@ -13,7 +13,8 @@ // limitations under the License. use crypto_provider::ed25519::{ - InvalidBytes, RawPrivateKey, RawPublicKey, RawSignature, Signature as _, SignatureError, + InvalidBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature, Signature as _, + SignatureError, }; use openssl::pkey::{Id, PKey, Private}; use openssl::sign::{Signer, Verifier}; @@ -32,7 +33,7 @@ type PublicKey = PublicKey; type Signature = Signature; - fn private_key(&self) -> RawPrivateKey { + fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey { let private_key = self.0.raw_private_key().unwrap(); let mut public_key = self.0.raw_public_key().unwrap(); let mut result = private_key; @@ -40,7 +41,7 @@ result.try_into().unwrap() } - fn from_private_key(bytes: &RawPrivateKey) -> Self { + fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self { Self(PKey::private_key_from_raw_bytes(bytes, Id::ED25519).unwrap()) }
diff --git a/nearby/crypto/crypto_provider_openssl/src/lib.rs b/nearby/crypto/crypto_provider_openssl/src/lib.rs index d8157d2..8d1db1d 100644 --- a/nearby/crypto/crypto_provider_openssl/src/lib.rs +++ b/nearby/crypto/crypto_provider_openssl/src/lib.rs
@@ -82,6 +82,8 @@ type Ed25519 = ed25519::Ed25519; type Aes128GcmSiv = crypto_provider_stubs::Aes128Stubs; type Aes256GcmSiv = crypto_provider_stubs::Aes256Stubs; + type Aes128Gcm = crypto_provider_stubs::Aes128Stubs; + type Aes256Gcm = crypto_provider_stubs::Aes256Stubs; type CryptoRng = OpenSslRng; fn constant_time_eq(a: &[u8], b: &[u8]) -> bool { @@ -113,7 +115,7 @@ use core::marker::PhantomData; use crypto_provider_test::sha2::*; - use crypto_provider_test::*; + use crypto_provider_test::{prelude::*, *}; use crate::Openssl;
diff --git a/nearby/crypto/crypto_provider_openssl/src/p256.rs b/nearby/crypto/crypto_provider_openssl/src/p256.rs index 2f5b0b1..17dd6ce 100644 --- a/nearby/crypto/crypto_provider_openssl/src/p256.rs +++ b/nearby/crypto/crypto_provider_openssl/src/p256.rs
@@ -12,8 +12,11 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret}; -use crypto_provider::p256::P256; +use crypto_provider::{ + elliptic_curve::{EcdhProvider, EphemeralSecret}, + p256::{PointCompression, P256}, + tinyvec::ArrayVec, +}; use openssl::bn::{BigNum, BigNumContext}; use openssl::derive::Deriver; use openssl::ec::{EcGroup, EcKey, EcPoint, PointConversionForm}; @@ -65,15 +68,24 @@ Ok(Self(eckey.try_into()?)) } - fn to_sec1_bytes(&self) -> Vec<u8> { + fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> { + let point_conversion_form = match point_compression { + PointCompression::Compressed => PointConversionForm::COMPRESSED, + PointCompression::Uncompressed => PointConversionForm::UNCOMPRESSED, + }; let ecgroup = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); let mut bncontext = BigNumContext::new().unwrap(); - self.0 - .ec_key() - .unwrap() - .public_key() - .to_bytes(&ecgroup, PointConversionForm::COMPRESSED, &mut bncontext) - .unwrap() + let mut bytes = ArrayVec::<[u8; 65]>::new(); + bytes.extend_from_slice( + &self + .0 + .ec_key() + .unwrap() + .public_key() + .to_bytes(&ecgroup, point_conversion_form, &mut bncontext) + .unwrap(), + ); + bytes } fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> { @@ -115,6 +127,7 @@ type Impl = P256Ecdh; type Error = Error; type Rng = (); + type EncodedPublicKey = ArrayVec<[u8; 65]>; fn generate_random(_rng: &mut Self::Rng) -> Self { let ecgroup = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); @@ -122,15 +135,20 @@ Self(eckey.try_into().unwrap()) } - fn public_key_bytes(&self) -> Vec<u8> { + fn public_key_bytes(&self) -> Self::EncodedPublicKey { let ecgroup = EcGroup::from_curve_name(Nid::X9_62_PRIME256V1).unwrap(); let mut bncontext = BigNumContext::new().unwrap(); - self.0 - .ec_key() - .unwrap() - .public_key() - .to_bytes(&ecgroup, PointConversionForm::COMPRESSED, &mut bncontext) - .unwrap() + let mut bytes = Self::EncodedPublicKey::new(); + bytes.extend_from_slice( + &self + .0 + .ec_key() + .unwrap() + .public_key() + .to_bytes(&ecgroup, PointConversionForm::COMPRESSED, &mut bncontext) + .unwrap(), + ); + bytes } fn diffie_hellman( @@ -178,7 +196,7 @@ use crypto_provider_test::p256::*; #[apply(p256_test_cases)] - fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>) { - testcase(PhantomData::<P256Ecdh>) + fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh>, _name: &str) { + testcase(PhantomData) } }
diff --git a/nearby/crypto/crypto_provider_openssl/src/x25519.rs b/nearby/crypto/crypto_provider_openssl/src/x25519.rs index ff6f3b0..2cb4cab 100644 --- a/nearby/crypto/crypto_provider_openssl/src/x25519.rs +++ b/nearby/crypto/crypto_provider_openssl/src/x25519.rs
@@ -30,14 +30,15 @@ impl PublicKey<X25519> for X25519PublicKey { type Error = ErrorStack; + type EncodedPublicKey = [u8; 32]; fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { let key = PKey::public_key_from_raw_bytes(bytes, Id::X25519)?; Ok(X25519PublicKey(key)) } - fn to_bytes(&self) -> Vec<u8> { - self.0.raw_public_key().unwrap() + fn to_bytes(&self) -> Self::EncodedPublicKey { + self.0.raw_public_key().unwrap().try_into().unwrap() } } @@ -48,14 +49,15 @@ type Impl = X25519Ecdh; type Error = ErrorStack; type Rng = (); + type EncodedPublicKey = [u8; 32]; fn generate_random(_rng: &mut Self::Rng) -> Self { let private_key = openssl::pkey::PKey::generate_x25519().unwrap(); Self(private_key) } - fn public_key_bytes(&self) -> Vec<u8> { - self.0.raw_public_key().unwrap() + fn public_key_bytes(&self) -> Self::EncodedPublicKey { + self.0.raw_public_key().unwrap().try_into().unwrap() } fn diffie_hellman(
diff --git a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml index e0bf534..a0638ff 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml +++ b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
@@ -6,15 +6,22 @@ [dependencies] aead = "0.5.1" -aes-gcm-siv = { version = "0.11.1", features = ["aes"] } -crypto_provider.workspace = true +aes-gcm-siv = { version = "0.11.1", features = [ + "aes", +], default-features = false } +aes-gcm = { version = "0.10.3", features = [ + "aes", +], default-features = false } +crypto_provider = { workspace = true } hmac.workspace = true hkdf.workspace = true sha2.workspace = true x25519-dalek.workspace = true p256 = { workspace = true, features = ["ecdh"], default-features = false } sec1.workspace = true -ed25519-dalek = { workspace = true, default-features = false, features = ["rand_core"] } +ed25519-dalek = { workspace = true, default-features = false, features = [ + "rand_core", +] } rand = { workspace = true, default-features = false } rand_core.workspace = true subtle.workspace = true @@ -31,5 +38,12 @@ [features] default = ["alloc", "rand_chacha"] -std = ["ed25519-dalek/default", "rand/std", "rand/std_rng", "crypto_provider/std", "crypto_provider/alloc"] -alloc = ["aead/bytes"] +std = [ + "alloc", + "ed25519-dalek/default", + "rand/std", + "rand/std_rng", + "crypto_provider/std", + "crypto_provider/alloc", +] +alloc = ["aead/bytes", "aead/alloc", "cbc/alloc", "crypto_provider/alloc"]
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm.rs new file mode 100644 index 0000000..f1d9e0f --- /dev/null +++ b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm.rs
@@ -0,0 +1,123 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(feature = "alloc")] +extern crate alloc; +#[cfg(feature = "alloc")] +use aead::Payload; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; + +// RustCrypto defined traits and types +use aes::cipher::typenum::consts::{U12, U16}; +use aes::cipher::BlockCipher; +use aes::cipher::BlockEncrypt; +#[cfg(feature = "alloc")] +use aes_gcm::aead::Aead as _; +use aes_gcm::aead::KeyInit; +use aes_gcm::AeadInPlace as _; + +// CryptoProvider traits and types +use crypto_provider::aead::{Aead, AeadError, AeadInit}; + +pub struct AesGcm<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit>( + aes_gcm::AesGcm<A, U12>, +); + +impl<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> crypto_provider::aead::AesGcm + for AesGcm<A> +{ +} + +impl<K: crypto_provider::aes::AesKey, A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> + AeadInit<K> for AesGcm<A> +{ + fn new(key: &K) -> Self { + Self(aes_gcm::AesGcm::<A, U12>::new(key.as_slice().into())) + } +} + +impl<A: aes::cipher::BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> Aead for AesGcm<A> { + const TAG_SIZE: usize = 16; + type Nonce = [u8; 12]; + type Tag = [u8; 16]; + + #[cfg(feature = "alloc")] + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0 + .encrypt(aes_gcm::Nonce::from_slice(nonce), Payload { msg, aad }) + .map_err(|_| AeadError) + } + + fn encrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + ) -> Result<Self::Tag, AeadError> { + self.0 + .encrypt_in_place_detached(aes_gcm::Nonce::from_slice(nonce), aad, msg) + .map(|arr| arr.into()) + .map_err(|_| AeadError) + } + + #[cfg(feature = "alloc")] + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0 + .decrypt(aes_gcm::Nonce::from_slice(nonce), Payload { msg, aad }) + .map_err(|_| AeadError) + } + + fn decrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + tag: &Self::Tag, + ) -> Result<(), AeadError> { + self.0 + .decrypt_in_place_detached(aes_gcm::Nonce::from_slice(nonce), aad, msg, tag.into()) + .map_err(|_| AeadError) + } +} + +#[cfg(test)] +mod tests { + use core::marker::PhantomData; + + use crypto_provider_test::aead::aes_gcm::*; + use crypto_provider_test::aes::*; + + use super::*; + + #[apply(aes_128_gcm_test_cases)] + fn aes_gcm_128_test(testcase: CryptoProviderTestCase<AesGcm<aes::Aes128>>) { + testcase(PhantomData); + } + + #[apply(aes_128_gcm_test_cases_detached)] + fn aes_128_gcm_test_detached(testcase: CryptoProviderTestCase<AesGcm<aes::Aes128>>) { + testcase(PhantomData); + } + + #[apply(aes_256_gcm_test_cases)] + fn aes_gcm_256_test(testcase: CryptoProviderTestCase<AesGcm<aes::Aes256>>) { + testcase(PhantomData); + } + + #[apply(aes_256_gcm_test_cases_detached)] + fn aes_256_gcm_test_detached(testcase: CryptoProviderTestCase<AesGcm<aes::Aes256>>) { + testcase(PhantomData); + } +}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs index 402c2ed..408017f 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/aead/aes_gcm_siv.rs
@@ -12,74 +12,118 @@ // See the License for the specific language governing permissions and // limitations under the License. -use aes_gcm_siv::{AeadInPlace, Aes128GcmSiv, Aes256GcmSiv, KeyInit, Nonce}; +#[cfg(feature = "alloc")] extern crate alloc; +#[cfg(feature = "alloc")] +use aead::Payload; +#[cfg(feature = "alloc")] use alloc::vec::Vec; -use crypto_provider::aead::{Aead, AeadError}; -use crypto_provider::aead::aes_gcm_siv::AesGcmSiv; -use crypto_provider::aes::{Aes128Key, Aes256Key, AesKey}; +// RustCrypto defined traits and types +use aes::cipher::typenum::consts::U16; +use aes::cipher::BlockCipher; +use aes::cipher::BlockEncrypt; +#[cfg(feature = "alloc")] +use aes_gcm_siv::aead::Aead as _; +use aes_gcm_siv::aead::KeyInit; +use aes_gcm_siv::AeadInPlace as _; -pub struct AesGcmSiv128(Aes128GcmSiv); +// CryptoProvider traits and types +use crypto_provider::aead::{Aead, AeadError, AeadInit}; -impl AesGcmSiv for AesGcmSiv128 {} +pub struct AesGcmSiv<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit>( + aes_gcm_siv::AesGcmSiv<A>, +); -impl Aead for AesGcmSiv128 { - const TAG_SIZE: usize = 16; - type Nonce = [u8; 12]; - type Key = Aes128Key; +impl<A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> crypto_provider::aead::AesGcmSiv + for AesGcmSiv<A> +{ +} - fn new(key: &Self::Key) -> Self { - Self(Aes128GcmSiv::new(key.as_slice().into())) - } - - fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { - self.0.encrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError) - } - - fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { - self.0.decrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError) +impl<K: crypto_provider::aes::AesKey, A: BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> + AeadInit<K> for AesGcmSiv<A> +{ + fn new(key: &K) -> Self { + Self(aes_gcm_siv::AesGcmSiv::<A>::new(key.as_slice().into())) } } -pub struct AesGcmSiv256(Aes256GcmSiv); - -impl AesGcmSiv for AesGcmSiv256 {} - -impl Aead for AesGcmSiv256 { +impl<A: aes::cipher::BlockCipher<BlockSize = U16> + BlockEncrypt + KeyInit> Aead for AesGcmSiv<A> { const TAG_SIZE: usize = 16; type Nonce = [u8; 12]; - type Key = Aes256Key; + type Tag = [u8; 16]; - fn new(key: &Self::Key) -> Self { - Self(Aes256GcmSiv::new(key.as_slice().into())) + #[cfg(feature = "alloc")] + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0 + .encrypt(aes_gcm_siv::Nonce::from_slice(nonce), Payload { msg, aad }) + .map_err(|_| AeadError) } - fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { - self.0.encrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError) + fn encrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + ) -> Result<Self::Tag, AeadError> { + self.0 + .encrypt_in_place_detached(aes_gcm_siv::Nonce::from_slice(nonce), aad, msg) + .map(|arr| arr.into()) + .map_err(|_| AeadError) } - fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { - self.0.decrypt_in_place(Nonce::from_slice(nonce), aad, msg).map_err(|_| AeadError) + #[cfg(feature = "alloc")] + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &Self::Nonce) -> Result<Vec<u8>, AeadError> { + self.0 + .decrypt(aes_gcm_siv::Nonce::from_slice(nonce), Payload { msg, aad }) + .map_err(|_| AeadError) + } + + fn decrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + tag: &Self::Tag, + ) -> Result<(), AeadError> { + self.0 + .decrypt_in_place_detached(aes_gcm_siv::Nonce::from_slice(nonce), aad, msg, tag.into()) + .map_err(|_| AeadError) } } #[cfg(test)] mod tests { use core::marker::PhantomData; - use crypto_provider_test::aead::aes_gcm_siv::*; use crypto_provider_test::aes::*; - - use super::*; + use crypto_provider_test::prelude::apply; #[apply(aes_128_gcm_siv_test_cases)] - fn aes_gcm_siv_128_test(testcase: CryptoProviderTestCase<AesGcmSiv128>) { + fn aes_gcm_siv_128_test( + testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes128>>, + ) { + testcase(PhantomData); + } + + #[apply(aes_128_gcm_siv_test_cases_detached)] + fn aes_gcm_siv_128_test_detached( + testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes128>>, + ) { testcase(PhantomData); } #[apply(aes_256_gcm_siv_test_cases)] - fn aes_gcm_siv_256_test(testcase: CryptoProviderTestCase<AesGcmSiv256>) { + fn aes_gcm_siv_256_test( + testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes256>>, + ) { + testcase(PhantomData); + } + + #[apply(aes_256_gcm_siv_test_cases_detached)] + fn aes_gcm_siv_256_test_detached( + testcase: CryptoProviderTestCase<crate::aead::aes_gcm_siv::AesGcmSiv<aes::Aes256>>, + ) { testcase(PhantomData); } }
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs index 7fc561b..e4c2c60 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/aead/mod.rs
@@ -13,3 +13,5 @@ // limitations under the License. pub(crate) mod aes_gcm_siv; + +pub(crate) mod aes_gcm;
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs deleted file mode 100644 index 06d7224..0000000 --- a/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs +++ /dev/null
@@ -1,53 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -extern crate alloc; -use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; -use aes::Aes256; -use alloc::vec::Vec; -use crypto_provider::aes::{ - cbc::{AesCbcIv, DecryptionError}, - Aes256Key, AesKey, -}; - -/// RustCrypto implementation of AES-CBC with PKCS7 padding -pub enum AesCbcPkcs7Padded {} -impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for AesCbcPkcs7Padded { - fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> { - let encryptor = cbc::Encryptor::<Aes256>::new(key.as_array().into(), iv.into()); - encryptor.encrypt_padded_vec_mut::<Pkcs7>(message) - } - - fn decrypt( - key: &Aes256Key, - iv: &AesCbcIv, - ciphertext: &[u8], - ) -> Result<Vec<u8>, DecryptionError> { - cbc::Decryptor::<Aes256>::new(key.as_array().into(), iv.into()) - .decrypt_padded_vec_mut::<Pkcs7>(ciphertext) - .map_err(|_| DecryptionError::BadPadding) - } -} - -#[cfg(test)] -mod tests { - use super::AesCbcPkcs7Padded; - use core::marker::PhantomData; - use crypto_provider_test::aes::cbc::*; - - #[apply(aes_256_cbc_test_cases)] - fn aes_256_cbc_test(testcase: CryptoProviderTestCase<AesCbcPkcs7Padded>) { - testcase(PhantomData); - } -}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/cbc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/cbc.rs new file mode 100644 index 0000000..c6d74f0 --- /dev/null +++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/cbc.rs
@@ -0,0 +1,102 @@ +// Copyright 2022 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#[cfg(feature = "alloc")] +extern crate alloc; + +use aes::cipher::{block_padding::Pkcs7, BlockDecryptMut, BlockEncryptMut, KeyIvInit}; +use aes::Aes256; +#[cfg(feature = "alloc")] +use alloc::vec::Vec; +use crypto_provider::{ + aes::{ + cbc::{AesCbcIv, DecryptionError, EncryptionError}, + Aes256Key, AesKey, + }, + tinyvec::SliceVec, +}; + +/// RustCrypto implementation of AES-CBC with PKCS7 padding +pub enum AesCbcPkcs7Padded {} +impl crypto_provider::aes::cbc::AesCbcPkcs7Padded for AesCbcPkcs7Padded { + #[cfg(feature = "alloc")] + fn encrypt(key: &Aes256Key, iv: &AesCbcIv, message: &[u8]) -> Vec<u8> { + let encryptor = cbc::Encryptor::<Aes256>::new(key.as_array().into(), iv.into()); + encryptor.encrypt_padded_vec_mut::<Pkcs7>(message) + } + + fn encrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + message: &mut SliceVec<u8>, + ) -> Result<(), EncryptionError> { + let encryptor = cbc::Encryptor::<Aes256>::new(key.as_array().into(), iv.into()); + let message_len = message.len(); + // Set the length so encrypt_padded_mut can write using the full capacity + // (Unlike `Vec.set_len`, `SliceVec.set_len` is safe and won't panic if len <= capacity) + message.set_len(message.capacity()); + encryptor + .encrypt_padded_mut::<Pkcs7>(message, message_len) + .map(|result| result.len()) + // `SliceVec.set_len` is safe, and won't panic because `encrypt_padded_mut` never + // returns a slice longer than the given buffer. + .map(|new_len| message.set_len(new_len)) + .map_err(|_| { + message.set_len(message_len); // Set the buffer back to its original length + EncryptionError::PaddingFailed + }) + } + + #[cfg(feature = "alloc")] + fn decrypt( + key: &Aes256Key, + iv: &AesCbcIv, + ciphertext: &[u8], + ) -> Result<Vec<u8>, DecryptionError> { + cbc::Decryptor::<Aes256>::new(key.as_array().into(), iv.into()) + .decrypt_padded_vec_mut::<Pkcs7>(ciphertext) + .map_err(|_| DecryptionError::BadPadding) + } + + fn decrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + ciphertext: &mut SliceVec<u8>, + ) -> Result<(), DecryptionError> { + // Decrypted size is always smaller than the input size because of padding, so we don't need + // to set the length to the full capacity. + cbc::Decryptor::<Aes256>::new(key.as_array().into(), iv.into()) + .decrypt_padded_mut::<Pkcs7>(ciphertext) + .map(|result| result.len()) + // `SliceVec.set_len` is safe, and won't panic because decrypted result length is always + // smaller than the input size. + .map(|new_len| ciphertext.set_len(new_len)) + .map_err(|_| { + ciphertext.as_mut().fill(0); + DecryptionError::BadPadding + }) + } +} + +#[cfg(test)] +mod tests { + use super::AesCbcPkcs7Padded; + use core::marker::PhantomData; + use crypto_provider_test::aes::cbc::*; + + #[apply(aes_256_cbc_test_cases)] + fn aes_256_cbc_test(testcase: CryptoProviderTestCase<AesCbcPkcs7Padded>) { + testcase(PhantomData); + } +}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/ctr.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/ctr.rs similarity index 88% rename from nearby/crypto/crypto_provider_rustcrypto/src/aes/ctr.rs rename to nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/ctr.rs index 85cdac6..09ae557 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/aes/ctr.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/ctr.rs
@@ -33,11 +33,7 @@ } } - fn encrypt(&mut self, data: &mut [u8]) { - self.cipher.apply_keystream(data); - } - - fn decrypt(&mut self, data: &mut [u8]) { + fn apply_keystream(&mut self, data: &mut [u8]) { self.cipher.apply_keystream(data); } } @@ -59,11 +55,7 @@ } } - fn encrypt(&mut self, data: &mut [u8]) { - self.cipher.apply_keystream(data); - } - - fn decrypt(&mut self, data: &mut [u8]) { + fn apply_keystream(&mut self, data: &mut [u8]) { self.cipher.apply_keystream(data); } }
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/mod.rs similarity index 98% rename from nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs rename to nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/mod.rs index c71f2ec..4c04bf5 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes_cp/mod.rs
@@ -22,7 +22,6 @@ }; /// Module implementing AES-CBC. -#[cfg(feature = "alloc")] pub(crate) mod cbc; pub(crate) mod ctr;
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs index d11a5ea..ce8089e 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
@@ -15,8 +15,8 @@ use ed25519_dalek::Signer; use crypto_provider::ed25519::{ - InvalidBytes, RawPrivateKey, RawPublicKey, RawSignature, Signature as _, SignatureError, - PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, + InvalidBytes, RawPrivateKey, RawPrivateKeyPermit, RawPublicKey, RawSignature, Signature as _, + SignatureError, PRIVATE_KEY_LENGTH, PUBLIC_KEY_LENGTH, SIGNATURE_LENGTH, }; pub struct Ed25519; @@ -33,11 +33,11 @@ type PublicKey = PublicKey; type Signature = Signature; - fn private_key(&self) -> [u8; PRIVATE_KEY_LENGTH] { + fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> [u8; PRIVATE_KEY_LENGTH] { self.0.to_bytes() } - fn from_private_key(bytes: &RawPrivateKey) -> Self { + fn from_raw_private_key(bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self { Self(ed25519_dalek::SigningKey::from_bytes(bytes)) }
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_cp.rs similarity index 86% rename from nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs rename to nearby/crypto/crypto_provider_rustcrypto/src/hkdf_cp.rs index aba0d4a..48380fa 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_cp.rs
@@ -22,7 +22,7 @@ use hmac::digest::{HashMarker, OutputSizeUser}; /// RustCrypto based hkdf implementation -pub struct Hkdf<D> +pub struct Hkdf<D>(hkdf::Hkdf<D>) where D: OutputSizeUser, D: CoreProxy, @@ -33,10 +33,7 @@ + Default + Clone, <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>, - Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero, -{ - hkdf_impl: hkdf::Hkdf<D>, -} + Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero; impl<D> crypto_provider::hkdf::Hkdf for Hkdf<D> where @@ -52,7 +49,7 @@ Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero, { fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self { - Hkdf { hkdf_impl: hkdf::Hkdf::new(salt, ikm) } + Hkdf(hkdf::Hkdf::new(salt, ikm)) } fn expand_multi_info( @@ -60,11 +57,11 @@ info_components: &[&[u8]], okm: &mut [u8], ) -> Result<(), InvalidLength> { - self.hkdf_impl.expand_multi_info(info_components, okm).map_err(|_| InvalidLength) + self.0.expand_multi_info(info_components, okm).map_err(|_| InvalidLength) } fn expand(&self, info: &[u8], okm: &mut [u8]) -> Result<(), InvalidLength> { - self.hkdf_impl.expand(info, okm).map_err(|_| InvalidLength) + self.0.expand(info, okm).map_err(|_| InvalidLength) } }
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/hmac_cp.rs similarity index 72% rename from nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs rename to nearby/crypto/crypto_provider_rustcrypto/src/hmac_cp.rs index dfda208..d8cef33 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/hmac_cp.rs
@@ -23,7 +23,7 @@ use hmac::Mac; /// RustCrypto based hmac implementation -pub struct Hmac<D> +pub struct Hmac<D>(hmac::Hmac<D>) where D: OutputSizeUser, D: CoreProxy, @@ -34,43 +34,38 @@ + Default + Clone, <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>, - Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero, -{ - hmac_impl: hmac::Hmac<D>, -} + Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero; impl crypto_provider::hmac::Hmac<32> for Hmac<sha2::Sha256> { #[allow(clippy::expect_used)] fn new_from_key(key: [u8; 32]) -> Self { hmac::Hmac::new_from_slice(&key) - .map(|hmac| Self { hmac_impl: hmac }) + .map(Self) .expect("length will always be valid because input key is of fixed size") } fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> { - hmac::Hmac::new_from_slice(key) - .map(|hmac| Self { hmac_impl: hmac }) - .map_err(|_| InvalidLength) + hmac::Hmac::new_from_slice(key).map(Self).map_err(|_| InvalidLength) } fn update(&mut self, data: &[u8]) { - self.hmac_impl.update(data); + self.0.update(data); } fn finalize(self) -> [u8; 32] { - self.hmac_impl.finalize().into_bytes().into() + self.0.finalize().into_bytes().into() } fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> { - self.hmac_impl.verify_slice(tag).map_err(|_| MacError) + self.0.verify_slice(tag).map_err(|_| MacError) } fn verify(self, tag: [u8; 32]) -> Result<(), MacError> { - self.hmac_impl.verify(&tag.into()).map_err(|_| MacError) + self.0.verify(&tag.into()).map_err(|_| MacError) } fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> { - self.hmac_impl.verify_truncated_left(tag).map_err(|_| MacError) + self.0.verify_truncated_left(tag).map_err(|_| MacError) } } @@ -78,34 +73,32 @@ #[allow(clippy::expect_used)] fn new_from_key(key: [u8; 64]) -> Self { hmac::Hmac::new_from_slice(&key) - .map(|hmac| Self { hmac_impl: hmac }) + .map(Self) .expect("length will always be valid because input key is of fixed size") } fn new_from_slice(key: &[u8]) -> Result<Self, InvalidLength> { - hmac::Hmac::new_from_slice(key) - .map(|hmac| Self { hmac_impl: hmac }) - .map_err(|_| InvalidLength) + hmac::Hmac::new_from_slice(key).map(Self).map_err(|_| InvalidLength) } fn update(&mut self, data: &[u8]) { - self.hmac_impl.update(data); + self.0.update(data); } fn finalize(self) -> [u8; 64] { - self.hmac_impl.finalize().into_bytes().into() + self.0.finalize().into_bytes().into() } fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> { - self.hmac_impl.verify_slice(tag).map_err(|_| MacError) + self.0.verify_slice(tag).map_err(|_| MacError) } fn verify(self, tag: [u8; 64]) -> Result<(), MacError> { - self.hmac_impl.verify(&tag.into()).map_err(|_| MacError) + self.0.verify(&tag.into()).map_err(|_| MacError) } fn verify_truncated_left(self, tag: &[u8]) -> Result<(), MacError> { - self.hmac_impl.verify_truncated_left(tag).map_err(|_| MacError) + self.0.verify_truncated_left(tag).map_err(|_| MacError) } }
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs index e95712c..628f679 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
@@ -25,6 +25,7 @@ use core::{fmt::Debug, marker::PhantomData}; +pub use aes; use cfg_if::cfg_if; pub use hkdf; pub use hmac; @@ -35,17 +36,17 @@ /// Contains the RustCrypto backed impls for AES-GCM-SIV operations mod aead; /// Contains the RustCrypto backed AES impl for CryptoProvider -pub mod aes; +pub mod aes_cp; /// Contains the RustCrypto backed impl for ed25519 key generation, signing, and verification mod ed25519; /// Contains the RustCrypto backed hkdf impl for CryptoProvider -mod hkdf_rc; +mod hkdf_cp; /// Contains the RustCrypto backed hmac impl for CryptoProvider -mod hmac_rc; +mod hmac_cp; /// Contains the RustCrypto backed P256 impl for CryptoProvider mod p256; /// Contains the RustCrypto backed SHA2 impl for CryptoProvider -mod sha2_rc; +mod sha2_cp; /// Contains the RustCrypto backed X25519 impl for CryptoProvider mod x25519; @@ -54,9 +55,11 @@ /// Providing a type alias for compatibility with existing usage of RustCrypto /// by default we use StdRng for the underlying csprng pub type RustCrypto = RustCryptoImpl<rand::rngs::StdRng>; - } else { + } else if #[cfg(feature = "rand_chacha")] { /// A no_std compatible implementation of CryptoProvider backed by RustCrypto crates pub type RustCrypto = RustCryptoImpl<rand_chacha::ChaCha20Rng>; + } else { + compile_error!("Must specify either --features std or --features rand_chacha"); } } @@ -76,23 +79,24 @@ impl<R: CryptoRng + SeedableRng + RngCore + Eq + PartialEq + Debug + Clone + Send> crypto_provider::CryptoProvider for RustCryptoImpl<R> { - type HkdfSha256 = hkdf_rc::Hkdf<sha2::Sha256>; - type HmacSha256 = hmac_rc::Hmac<sha2::Sha256>; - type HkdfSha512 = hkdf_rc::Hkdf<sha2::Sha512>; - type HmacSha512 = hmac_rc::Hmac<sha2::Sha512>; - #[cfg(feature = "alloc")] - type AesCbcPkcs7Padded = aes::cbc::AesCbcPkcs7Padded; + type HkdfSha256 = hkdf_cp::Hkdf<sha2::Sha256>; + type HmacSha256 = hmac_cp::Hmac<sha2::Sha256>; + type HkdfSha512 = hkdf_cp::Hkdf<sha2::Sha512>; + type HmacSha512 = hmac_cp::Hmac<sha2::Sha512>; + type AesCbcPkcs7Padded = aes_cp::cbc::AesCbcPkcs7Padded; type X25519 = x25519::X25519Ecdh<R>; type P256 = p256::P256Ecdh<R>; - type Sha256 = sha2_rc::RustCryptoSha256; - type Sha512 = sha2_rc::RustCryptoSha512; - type Aes128 = aes::Aes128; - type Aes256 = aes::Aes256; - type AesCtr128 = aes::ctr::AesCtr128; - type AesCtr256 = aes::ctr::AesCtr256; + type Sha256 = sha2_cp::RustCryptoSha256; + type Sha512 = sha2_cp::RustCryptoSha512; + type Aes128 = aes_cp::Aes128; + type Aes256 = aes_cp::Aes256; + type AesCtr128 = aes_cp::ctr::AesCtr128; + type AesCtr256 = aes_cp::ctr::AesCtr256; type Ed25519 = ed25519::Ed25519; - type Aes128GcmSiv = aead::aes_gcm_siv::AesGcmSiv128; - type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv256; + type Aes128GcmSiv = aead::aes_gcm_siv::AesGcmSiv<aes::Aes128>; + type Aes256GcmSiv = aead::aes_gcm_siv::AesGcmSiv<aes::Aes256>; + type Aes128Gcm = aead::aes_gcm::AesGcm<aes::Aes128>; + type Aes256Gcm = aead::aes_gcm::AesGcm<aes::Aes256>; type CryptoRng = RcRng<R>; fn constant_time_eq(a: &[u8], b: &[u8]) -> bool { @@ -124,6 +128,7 @@ mod tests { use core::marker::PhantomData; + use crypto_provider_test::prelude::*; use crypto_provider_test::sha2::*; use crate::RustCrypto;
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs b/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs index 539ccc0..98a6cd4 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs
@@ -12,14 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate alloc; - use crate::RcRng; -use alloc::vec::Vec; use core::marker::PhantomData; use crypto_provider::{ elliptic_curve::{EcdhProvider, EphemeralSecret}, - p256::P256, + p256::{PointCompression, P256}, + tinyvec::ArrayVec, }; use p256::{ elliptic_curve, @@ -52,8 +50,12 @@ p256::PublicKey::from_sec1_bytes(bytes).map(Self) } - fn to_sec1_bytes(&self) -> Vec<u8> { - self.0.to_encoded_point(true).as_bytes().to_vec() + fn to_sec1_bytes(&self, point_compression: PointCompression) -> ArrayVec<[u8; 65]> { + let mut bytes = ArrayVec::<[u8; 65]>::new(); + bytes.extend_from_slice( + self.0.to_encoded_point(point_compression == PointCompression::Compressed).as_bytes(), + ); + bytes } #[allow(clippy::expect_used)] @@ -87,6 +89,7 @@ type Impl = P256Ecdh<R>; type Error = sec1::Error; type Rng = RcRng<R>; + type EncodedPublicKey = ArrayVec<[u8; 65]>; fn generate_random(rng: &mut Self::Rng) -> Self { Self { @@ -95,8 +98,10 @@ } } - fn public_key_bytes(&self) -> Vec<u8> { - self.secret.public_key().to_encoded_point(false).as_bytes().into() + fn public_key_bytes(&self) -> Self::EncodedPublicKey { + let mut bytes = Self::EncodedPublicKey::new(); + bytes.extend_from_slice(self.secret.public_key().to_encoded_point(false).as_bytes()); + bytes } fn diffie_hellman( @@ -136,7 +141,7 @@ use rand::rngs::StdRng; #[apply(p256_test_cases)] - fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>) { + fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>, _name: &str) { testcase(PhantomData::<P256Ecdh<StdRng>>) } }
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/sha2_cp.rs similarity index 100% rename from nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs rename to nearby/crypto/crypto_provider_rustcrypto/src/sha2_cp.rs
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs b/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs index 445e858..ad67777 100644 --- a/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs +++ b/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs
@@ -12,10 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate alloc; - use crate::RcRng; -use alloc::vec::Vec; use core::marker::PhantomData; use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey}; use crypto_provider::x25519::X25519; @@ -45,6 +42,7 @@ type Impl = X25519Ecdh<R>; type Error = Error; type Rng = RcRng<R>; + type EncodedPublicKey = [u8; 32]; fn generate_random(rng: &mut Self::Rng) -> Self { Self { @@ -53,9 +51,9 @@ } } - fn public_key_bytes(&self) -> Vec<u8> { + fn public_key_bytes(&self) -> Self::EncodedPublicKey { let pubkey: x25519_dalek::PublicKey = (&self.secret).into(); - pubkey.to_bytes().into() + pubkey.to_bytes() } fn diffie_hellman( @@ -90,14 +88,15 @@ impl PublicKey<X25519> for X25519PublicKey { type Error = Error; + type EncodedPublicKey = [u8; 32]; fn from_bytes(bytes: &[u8]) -> Result<Self, Self::Error> { let byte_sized: [u8; 32] = bytes.try_into().map_err(|_| Error::WrongSize)?; Ok(Self(byte_sized.into())) } - fn to_bytes(&self) -> Vec<u8> { - self.0.as_bytes().to_vec() + fn to_bytes(&self) -> Self::EncodedPublicKey { + self.0.to_bytes() } }
diff --git a/nearby/crypto/crypto_provider_stubs/Cargo.toml b/nearby/crypto/crypto_provider_stubs/Cargo.toml index d7c4d45..4e8bdec 100644 --- a/nearby/crypto/crypto_provider_stubs/Cargo.toml +++ b/nearby/crypto/crypto_provider_stubs/Cargo.toml
@@ -5,4 +5,4 @@ publish.workspace = true [dependencies] -crypto_provider = {workspace = true, features = ["std", "alloc"] } \ No newline at end of file +crypto_provider = {workspace = true, features = ["std", "alloc"] }
diff --git a/nearby/crypto/crypto_provider_stubs/src/lib.rs b/nearby/crypto/crypto_provider_stubs/src/lib.rs index 5d54b72..32c9381 100644 --- a/nearby/crypto/crypto_provider_stubs/src/lib.rs +++ b/nearby/crypto/crypto_provider_stubs/src/lib.rs
@@ -20,21 +20,23 @@ use std::fmt::Debug; -use crypto_provider::ed25519::{RawPrivateKey, RawPublicKey, RawSignature}; +use crypto_provider::aead::AeadInit; use crypto_provider::{ - aead::aes_gcm_siv::AesGcmSiv, - aead::{Aead, AeadError}, + aead::{Aead, AeadError, AesGcm, AesGcmSiv}, aes::{ - cbc::{AesCbcIv, AesCbcPkcs7Padded, DecryptionError}, + cbc::{AesCbcIv, AesCbcPkcs7Padded, DecryptionError, EncryptionError}, ctr::{AesCtr, NonceAndCounter}, Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, }, - ed25519, - ed25519::{Ed25519Provider, InvalidBytes, KeyPair, Signature, SignatureError}, + ed25519::{ + self, Ed25519Provider, InvalidBytes, KeyPair, RawPrivateKey, RawPrivateKeyPermit, + RawPublicKey, RawSignature, Signature, SignatureError, + }, elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey}, hkdf::{Hkdf, InvalidLength}, hmac::{Hmac, MacError}, - p256::{P256PublicKey, P256}, + p256::{P256PublicKey, PointCompression, P256}, + tinyvec::{ArrayVec, SliceVec}, x25519::X25519, }; @@ -58,6 +60,8 @@ type Ed25519 = Ed25519Stubs; type Aes128GcmSiv = Aes128Stubs; type Aes256GcmSiv = Aes256Stubs; + type Aes128Gcm = Aes128Stubs; + type Aes256Gcm = Aes256Stubs; type CryptoRng = (); fn constant_time_eq(_a: &[u8], _b: &[u8]) -> bool { @@ -171,6 +175,14 @@ unimplemented!() } + fn encrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + message: &mut SliceVec<u8>, + ) -> Result<(), EncryptionError> { + unimplemented!() + } + fn decrypt( _key: &Aes256Key, _iv: &AesCbcIv, @@ -178,6 +190,14 @@ ) -> Result<Vec<u8>, DecryptionError> { unimplemented!() } + + fn decrypt_in_place( + key: &Aes256Key, + iv: &AesCbcIv, + ciphertext: &mut SliceVec<u8>, + ) -> Result<(), DecryptionError> { + unimplemented!() + } } pub struct X25519Stubs; @@ -194,12 +214,13 @@ type Impl = X25519Stubs; type Error = (); type Rng = (); + type EncodedPublicKey = [u8; 32]; fn generate_random(_rng: &mut Self::Rng) -> Self { unimplemented!() } - fn public_key_bytes(&self) -> Vec<u8> { + fn public_key_bytes(&self) -> Self::EncodedPublicKey { unimplemented!() } @@ -215,12 +236,13 @@ type Impl = P256Stubs; type Error = (); type Rng = (); + type EncodedPublicKey = ArrayVec<[u8; 65]>; fn generate_random(_rng: &mut Self::Rng) -> Self { unimplemented!() } - fn public_key_bytes(&self) -> Vec<u8> { + fn public_key_bytes(&self) -> Self::EncodedPublicKey { unimplemented!() } @@ -237,12 +259,13 @@ impl PublicKey<X25519> for EcdhPubKey { type Error = (); + type EncodedPublicKey = [u8; 32]; fn from_bytes(_bytes: &[u8]) -> Result<Self, Self::Error> { unimplemented!() } - fn to_bytes(&self) -> Vec<u8> { + fn to_bytes(&self) -> Self::EncodedPublicKey { unimplemented!() } } @@ -257,7 +280,7 @@ unimplemented!() } - fn to_sec1_bytes(&self) -> Vec<u8> { + fn to_sec1_bytes(&self, _point_compression: PointCompression) -> ArrayVec<[u8; 65]> { unimplemented!() } @@ -294,6 +317,12 @@ pub struct Aes128Stubs; +impl AeadInit<Aes128Key> for Aes128Stubs { + fn new(key: &Aes128Key) -> Self { + unimplemented!() + } +} + impl AesCipher for Aes128Stubs { type Key = Aes128Key; @@ -321,11 +350,7 @@ unimplemented!() } - fn encrypt(&mut self, _data: &mut [u8]) { - unimplemented!() - } - - fn decrypt(&mut self, _data: &mut [u8]) { + fn apply_keystream(&mut self, data: &mut [u8]) { unimplemented!() } } @@ -333,25 +358,48 @@ impl Aead for Aes128Stubs { const TAG_SIZE: usize = 16; type Nonce = [u8; 12]; - type Key = Aes128Key; + type Tag = [u8; 16]; - fn new(key: &Self::Key) -> Self { + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> { unimplemented!() } - fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { + fn encrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + ) -> Result<Self::Tag, AeadError> { unimplemented!() } - fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> { + unimplemented!() + } + + fn decrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + tag: &Self::Tag, + ) -> Result<(), AeadError> { unimplemented!() } } impl AesGcmSiv for Aes128Stubs {} +impl AesGcm for Aes128Stubs {} + pub struct Aes256Stubs; +impl AeadInit<Aes256Key> for Aes256Stubs { + fn new(key: &Aes256Key) -> Self { + unimplemented!() + } +} + impl AesCipher for Aes256Stubs { type Key = Aes256Key; @@ -379,11 +427,7 @@ unimplemented!() } - fn encrypt(&mut self, _data: &mut [u8]) { - unimplemented!() - } - - fn decrypt(&mut self, _data: &mut [u8]) { + fn apply_keystream(&mut self, data: &mut [u8]) { unimplemented!() } } @@ -391,23 +435,40 @@ impl Aead for Aes256Stubs { const TAG_SIZE: usize = 16; type Nonce = [u8; 12]; - type Key = Aes256Key; + type Tag = [u8; 16]; - fn new(key: &Self::Key) -> Self { + fn encrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> { unimplemented!() } - fn encrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { + fn encrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + ) -> Result<Self::Tag, AeadError> { unimplemented!() } - fn decrypt(&self, msg: &mut Vec<u8>, aad: &[u8], nonce: &[u8; 12]) -> Result<(), AeadError> { + fn decrypt(&self, msg: &[u8], aad: &[u8], nonce: &[u8; 12]) -> Result<Vec<u8>, AeadError> { + unimplemented!() + } + + fn decrypt_detached( + &self, + msg: &mut [u8], + aad: &[u8], + nonce: &Self::Nonce, + tag: &Self::Tag, + ) -> Result<(), AeadError> { unimplemented!() } } impl AesGcmSiv for Aes256Stubs {} +impl AesGcm for Aes256Stubs {} + pub struct Ed25519Stubs; impl Ed25519Provider for Ed25519Stubs { @@ -457,11 +518,11 @@ type PublicKey = PublicKeyStubs; type Signature = SignatureStubs; - fn private_key(&self) -> RawPrivateKey { + fn raw_private_key(&self, _permit: &RawPrivateKeyPermit) -> RawPrivateKey { unimplemented!() } - fn from_private_key(_bytes: &RawPrivateKey) -> Self + fn from_raw_private_key(_bytes: &RawPrivateKey, _permit: &RawPrivateKeyPermit) -> Self where Self: Sized, {
diff --git a/nearby/crypto/crypto_provider_test/Cargo.toml b/nearby/crypto/crypto_provider_test/Cargo.toml index a4d92ec..4064330 100644 --- a/nearby/crypto/crypto_provider_test/Cargo.toml +++ b/nearby/crypto/crypto_provider_test/Cargo.toml
@@ -5,7 +5,7 @@ publish.workspace = true [dependencies] -crypto_provider = { workspace = true, features = ["test_vectors"] } +crypto_provider = { workspace = true, features = ["raw_private_key_permit", "test_vectors", "alloc"] } rand_ext.workspace = true test_helper.workspace = true @@ -14,4 +14,4 @@ rstest.workspace = true rstest_reuse.workspace = true wycheproof.workspace = true -hex.workspace = true \ No newline at end of file +hex.workspace = true
diff --git a/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock b/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock index 9e1d140..bd8f6f9 100644 --- a/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock +++ b/nearby/crypto/crypto_provider_test/fuzz/Cargo.lock
@@ -25,6 +25,20 @@ ] [[package]] +name = "aes-gcm" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + +[[package]] name = "aes-gcm-siv" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -165,6 +179,9 @@ [[package]] name = "crypto_provider" version = "0.1.0" +dependencies = [ + "tinyvec", +] [[package]] name = "crypto_provider_default" @@ -193,6 +210,7 @@ dependencies = [ "aead", "aes", + "aes-gcm", "aes-gcm-siv", "cbc", "cfg-if", @@ -239,9 +257,9 @@ [[package]] name = "curve25519-dalek" -version = "4.0.0-rc.3" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436ace70fc06e06f7f689d2624dc4e2f0ea666efb5aa704215f7249ae6e047a7" +checksum = "f711ade317dd348950a9910f81c5947e3d8907ebd2b83f76203ff1807e6a2bc2" dependencies = [ "cfg-if", "cpufeatures", @@ -307,9 +325,9 @@ [[package]] name = "ed25519-dalek" -version = "2.0.0-rc.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa8e9049d5d72bfc12acbc05914731b5322f79b5e2f195e9f2d705fca22ab4c" +checksum = "7277392b266383ef8396db7fdeb1e77b6c52fed775f5df15bb24f35b72156980" dependencies = [ "curve25519-dalek", "ed25519", @@ -390,6 +408,16 @@ ] [[package]] +name = "ghash" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d930750de5717d2dd0b8c0d42c076c0e884c81a73e6cab859bbd2339c71e3e40" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] name = "group" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -732,6 +760,12 @@ ] [[package]] +name = "tinyvec" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" + +[[package]] name = "typenum" version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -773,9 +807,9 @@ [[package]] name = "x25519-dalek" -version = "2.0.0-rc.3" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec7fae07da688e17059d5886712c933bb0520f15eff2e09cfa18e30968f4e63a" +checksum = "fb66477291e7e8d2b0ff1bcb900bf29489a9692816d79874bea351e7a8b6de96" dependencies = [ "curve25519-dalek", "rand_core",
diff --git a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs new file mode 100644 index 0000000..88eaf32 --- /dev/null +++ b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm.rs
@@ -0,0 +1,316 @@ +// Copyright 2023 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +use alloc::vec::Vec; +use core::marker; + +use hex_literal::hex; +use rstest_reuse::template; + +pub use crate::prelude; +use crypto_provider::aead::{AeadInit, AesGcm}; + +/// Test AES-GCM-128 encryption +pub fn aes_128_gcm_test_encrypt<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC4 + let test_key = hex!("bedcfb5a011ebc84600fcb296c15af0d"); + let nonce = hex!("438a547a94ea88dce46c6c85"); + let aes = A::new(&test_key.into()); + let msg = hex!(""); + let tag = hex!("960247ba5cde02e41a313c4c0136edc3"); + let result = aes.encrypt(&msg, b"", &nonce).expect("Should succeed"); + assert_eq!(&result[..], &tag); +} + +pub fn aes_128_gcm_test_encrypt_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC4 + let test_key = hex!("bedcfb5a011ebc84600fcb296c15af0d"); + let nonce = hex!("438a547a94ea88dce46c6c85"); + let aes = A::new(&test_key.into()); + let msg = hex!(""); + let tag = hex!("960247ba5cde02e41a313c4c0136edc3"); + let mut buf = Vec::from(msg.as_slice()); + let actual_tag: [u8; 16] = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap(); + assert_eq!(&buf, &[0_u8; 0]); + assert_eq!(&actual_tag, &tag); +} + +/// Test AES-GCM-128 decryption +pub fn aes_128_gcm_test_decrypt<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC2 + let test_key = hex!("5b9604fe14eadba931b0ccf34843dab9"); + let nonce = hex!("921d2507fa8007b7bd067d34"); + let aes = A::new(&test_key.into()); + let msg = hex!("001d0c231287c1182784554ca3a21908"); + let ct = hex!("49d8b9783e911913d87094d1f63cc765"); + let ad = hex!("00112233445566778899aabbccddeeff"); + let tag = hex!("1e348ba07cca2cf04c618cb4d43a5b92"); + let result = aes.encrypt(&msg, &ad, &nonce).expect("should succeed"); + assert_eq!(&result[..16], &ct); + assert_eq!(&result[16..], &tag); + assert_eq!(A::TAG_SIZE, result[16..].len()); + let result = aes.decrypt(&result[..], &ad, &nonce).expect("should succeed"); + assert_eq!(&result[..], &msg); +} + +/// Test AES-GCM-128 decryption +pub fn aes_128_gcm_test_decrypt_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC2 + let test_key = hex!("5b9604fe14eadba931b0ccf34843dab9"); + let nonce = hex!("921d2507fa8007b7bd067d34"); + let aes = A::new(&test_key.into()); + let msg = hex!("001d0c231287c1182784554ca3a21908"); + let ct = hex!("49d8b9783e911913d87094d1f63cc765"); + let ad = hex!("00112233445566778899aabbccddeeff"); + let tag = hex!("1e348ba07cca2cf04c618cb4d43a5b92"); + let mut buf = Vec::from(msg.as_slice()); + let actual_tag = aes.encrypt_detached(&mut buf, &ad, &nonce).unwrap(); + assert_eq!(&buf, &ct); + assert_eq!(actual_tag, tag); + assert!(aes.decrypt_detached(&mut buf, &ad, &nonce, &tag).is_ok()); + assert_eq!(&buf[..], &msg); +} + +/// Test AES-GCM-128 decryption +pub fn aes_128_gcm_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC23 + let test_key = hex!("000102030405060708090a0b0c0d0e0f"); + let nonce = hex!("505152535455565758595a5b"); + let aes = A::new(&test_key.into()); + let ct = hex!("eb156d081ed6b6b55f4612f021d87b39"); + let mut buf = Vec::from(ct.as_slice()); + let bad_tag = hex!("d9847dbc326a06e988c77ad3863e6083"); + aes.decrypt_detached(&mut buf, b"", &nonce, &bad_tag) + .expect_err("decryption tag verification should fail"); + // assert that the buffer does not change if tag verification fails + assert_eq!(buf, ct); +} + +/// Test AES-256-GCM encryption/decryption +pub fn aes_256_gcm_test_tc74<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC74 + let test_key = hex!("29d3a44f8723dc640239100c365423a312934ac80239212ac3df3421a2098123"); + let nonce = hex!("00112233445566778899aabb"); + let aes = A::new(&test_key.into()); + let msg = hex!(""); + let ad = hex!("aabbccddeeff"); + let tag = hex!("2a7d77fa526b8250cb296078926b5020"); + let result = aes.encrypt(&msg, &ad, &nonce).expect("should succeed"); + assert_eq!(&result[..], &tag); + assert_eq!(A::TAG_SIZE, result.len()); + let result = aes.decrypt(&result, &ad, &nonce).expect("should succeed"); + assert_eq!(&result[..], &msg); +} + +/// Test AES-256-GCM encryption/decryption +pub fn aes_256_gcm_test_tc74_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC74 + let test_key = hex!("29d3a44f8723dc640239100c365423a312934ac80239212ac3df3421a2098123"); + let nonce = hex!("00112233445566778899aabb"); + let aes = A::new(&test_key.into()); + let msg = hex!(""); + let ad = hex!("aabbccddeeff"); + let ct = hex!(""); + let tag = hex!("2a7d77fa526b8250cb296078926b5020"); + let mut buf = Vec::new(); + buf.extend_from_slice(&msg); + let actual_tag = aes.encrypt_detached(&mut buf, &ad, &nonce).unwrap(); + assert_eq!(&buf, &ct); + assert_eq!(&actual_tag, &tag); + assert_eq!(A::TAG_SIZE, tag.len()); + assert!(aes.decrypt_detached(&mut buf, &ad, &nonce, &actual_tag).is_ok()); + assert_eq!(&buf, &msg); +} + +/// Test AES-256-GCM encryption/decryption +pub fn aes_256_gcm_test_tc79<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC79 + let test_key = hex!("59d4eafb4de0cfc7d3db99a8f54b15d7b39f0acc8da69763b019c1699f87674a"); + let nonce = hex!("2fcb1b38a99e71b84740ad9b"); + let aes = A::new(&test_key.into()); + let msg = hex!("549b365af913f3b081131ccb6b825588"); + let ct = hex!("f58c16690122d75356907fd96b570fca"); + let tag = hex!("28752c20153092818faba2a334640d6e"); + let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed"); + assert_eq!(&result[..16], &ct); + assert_eq!(&result[16..], &tag); + let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed"); + assert_eq!(&result[..], &msg); +} + +/// Test AES-256-GCM encryption/decryption +pub fn aes_256_gcm_test_tc79_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC79 + let test_key = hex!("59d4eafb4de0cfc7d3db99a8f54b15d7b39f0acc8da69763b019c1699f87674a"); + let nonce = hex!("2fcb1b38a99e71b84740ad9b"); + let aes = A::new(&test_key.into()); + let msg = hex!("549b365af913f3b081131ccb6b825588"); + let ct = hex!("f58c16690122d75356907fd96b570fca"); + let tag = hex!("28752c20153092818faba2a334640d6e"); + let mut buf = Vec::from(msg.as_slice()); + let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap(); + assert_eq!(&buf, &ct); + assert_eq!(&actual_tag, &tag); + assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &tag).is_ok()); + assert_eq!(&buf, &msg); +} + +/// Test AES-256-GCM encryption/decryption where the tag given to decryption doesn't match. +pub fn aes_256_gcm_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>) +where + A: AesGcm<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_test.json + // TC94 + let test_key = hex!("000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f"); + let nonce = hex!("505152535455565758595a5b"); + let aes = A::new(&test_key.into()); + let aad = hex!(""); + let ct = hex!("b2061457c0759fc1749f174ee1ccadfa"); + let bad_tag = hex!("9de8fef6d8ab1bf1bf887232eab590dd"); + let mut buf = Vec::from(ct.as_slice()); + aes.decrypt_detached(&mut buf, &aad, &nonce, &bad_tag) + .expect_err("Decrypting with bad tag should fail"); + // assert that the buffer does not change if tag verification fails + assert_eq!(buf, ct); +} + +/// Generates the test cases to validate the AES-128-GCM implementation. +/// For example, to test `MyAesGcm128Impl`: +/// +/// ``` +/// use crypto_provider::aes::aes_gcm::testing::*; +/// +/// mod tests { +/// #[apply(aes_128_gcm_test_cases)] +/// fn aes_128_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcmImpl>) { +/// testcase(MyAesGcm128Impl); +/// } +/// } +/// ``` +#[template] +#[export] +#[rstest] +#[case::encrypt(aes_128_gcm_test_encrypt)] +#[case::decrypt(aes_128_gcm_test_decrypt)] +fn aes_128_gcm_test_cases<F: AesGcmFactory<Key = Aes128Key>>( + #[case] testcase: CryptoProviderTestCase<F>, +) { +} + +/// Generates the test cases to validate the AES-128-GCM implementation. +/// For example, to test `MyAesGcm128Impl`: +/// +/// ``` +/// use crypto_provider::aes::aes_gcm::testing::*; +/// +/// mod tests { +/// #[apply(aes_128_gcm_test_cases_detached)] +/// fn aes_128_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcmImpl>) { +/// testcase(MyAesGcm128Impl); +/// } +/// } +/// ``` +#[template] +#[export] +#[rstest] +#[case::encrypt_detached(aes_128_gcm_test_encrypt_detached)] +#[case::decrypt_detached(aes_128_gcm_test_decrypt_detached)] +#[case::decrypt_detached_bad_tag(aes_128_gcm_test_decrypt_detached_bad_tag)] +fn aes_128_gcm_test_cases_detached<F: AesGcmFactory<Key = Aes128Key>>( + #[case] testcase: CryptoProviderTestCase<F>, +) { +} + +/// Generates the test cases to validate the AES-256-GCM implementation. +/// For example, to test `MyAesGcm256Impl`: +/// +/// ``` +/// use crypto_provider::aes::aes_gcm::testing::*; +/// +/// mod tests { +/// #[apply(aes_256_gcm_test_cases)] +/// fn aes_256_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcm256Impl>) { +/// testcase(MyAesGcm256Impl); +/// } +/// } +/// ``` +#[template] +#[export] +#[rstest] +#[case::tc74(aes_256_gcm_test_tc74)] +#[case::tc79(aes_256_gcm_test_tc79)] +fn aes_256_gcm_test_cases<F: AesGcmFactory<Key = Aes256Key>>( + #[case] testcase: CryptoProviderTestCase<F>, +) { +} + +/// Generates the test cases to validate the AES-256-GCM implementation. +/// For example, to test `MyAesGcm256Impl`: +/// +/// ``` +/// use crypto_provider::aes::aes_gcm::testing::*; +/// +/// mod tests { +/// #[apply(aes_256_gcm_test_cases_detached)] +/// fn aes_256_gcm_tests(testcase: CryptoProviderTestCase<MyAesGcm256Impl>) { +/// testcase(MyAesGcm256Impl); +/// } +/// } +/// ``` +#[template] +#[export] +#[rstest] +#[case::tc74_detached(aes_256_gcm_test_tc74_detached)] +#[case::tc79_detached(aes_256_gcm_test_tc79_detached)] +#[case::decrypt_detached_bad_tag(aes_256_gcm_test_decrypt_detached_bad_tag)] +fn aes_256_gcm_test_cases_detached<F: AesGcmFactory<Key = Aes256Key>>( + #[case] testcase: CryptoProviderTestCase<F>, +) { +}
diff --git a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs index 893fba2..56d2215 100644 --- a/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs +++ b/nearby/crypto/crypto_provider_test/src/aead/aes_gcm_siv.rs
@@ -18,12 +18,28 @@ use rstest_reuse::template; pub use crate::prelude; -use crypto_provider::aes::{Aes128Key, Aes256Key}; +use crypto_provider::aead::{AeadInit, AesGcmSiv}; -use crypto_provider::aead::aes_gcm_siv::AesGcmSiv; +/// Test AES-GCM-SIV-128 encryption +pub fn aes_128_gcm_siv_test_encrypt<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json + // TC1 + let test_key = hex!("01000000000000000000000000000000"); + let nonce = hex!("030000000000000000000000"); + let aes = A::new(&test_key.into()); + let msg = hex!(""); + let tag = hex!("dc20e2d83f25705bb49e439eca56de25"); + let result = aes.encrypt(&msg, b"", &nonce).expect("Should succeed"); + assert_eq!(&result[..], &tag); +} -/// Test AES-GCM-SIV-128 encryption/decryption -pub fn aes_128_gcm_siv_test<A: AesGcmSiv<Key = Aes128Key>>(_marker: marker::PhantomData<A>) { +pub fn aes_128_gcm_siv_test_encrypt_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json // TC1 let test_key = hex!("01000000000000000000000000000000"); @@ -32,23 +48,102 @@ let msg = hex!(""); let mut buf = Vec::from(msg.as_slice()); let tag = hex!("dc20e2d83f25705bb49e439eca56de25"); - assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok()); - assert_eq!(&buf[..], &tag); + let actual_tag: [u8; 16] = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap(); + assert_eq!(&buf, &[0_u8; 0]); + assert_eq!(&actual_tag, &tag); +} + +/// Test AES-GCM-SIV-128 decryption +pub fn aes_128_gcm_siv_test_decrypt<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json // TC2 + let test_key = hex!("01000000000000000000000000000000"); + let nonce = hex!("030000000000000000000000"); + let aes = A::new(&test_key.into()); + let msg = hex!("0100000000000000"); + let ct = hex!("b5d839330ac7b786"); + let tag = hex!("578782fff6013b815b287c22493a364c"); + let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed"); + assert_eq!(&result[..8], &ct); + assert_eq!(&result[8..], &tag); + assert_eq!(A::TAG_SIZE, result[8..].len()); + let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed"); + assert_eq!(&result[..], &msg); +} + +/// Test AES-GCM-SIV-128 decryption +pub fn aes_128_gcm_siv_test_decrypt_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json + // TC2 + let test_key = hex!("01000000000000000000000000000000"); + let nonce = hex!("030000000000000000000000"); + let aes = A::new(&test_key.into()); let msg = hex!("0100000000000000"); let ct = hex!("b5d839330ac7b786"); let tag = hex!("578782fff6013b815b287c22493a364c"); let mut buf = Vec::from(msg.as_slice()); - assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok()); - assert_eq!(&buf[..8], &ct); - assert_eq!(&buf[8..], &tag); - assert_eq!(A::TAG_SIZE, buf[8..].len()); - assert!(aes.decrypt(&mut buf, b"", &nonce).is_ok()); + let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap(); + assert_eq!(&buf, &ct); + assert_eq!(actual_tag, tag); + assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &tag).is_ok()); assert_eq!(&buf[..], &msg); } +/// Test AES-GCM-SIV-128 decryption where the tag given to decryption doesn't match +pub fn aes_128_gcm_siv_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes128Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json + // TC45 + let test_key = hex!("00112233445566778899aabbccddeeff"); + let nonce = hex!("000000000000000000000000"); + let aad = hex!("9ea3371e258288d5a01b15384e2c99ee"); + let aes = A::new(&test_key.into()); + // Use a longer ciphertext as the test case to make sure it's not unchanged only for the most + // recent block. + let ct = hex!( + "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" + "00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff" + ); + let bad_tag = hex!("13a1883272188b4c8d2727178198fe95"); + let mut buf = Vec::from(ct.as_slice()); + aes.decrypt_detached(&mut buf, &aad, &nonce, &bad_tag).expect_err("Decryption should fail"); + assert_eq!(&buf, &ct); // Buffer should be unchanged if decryption failed +} + /// Test AES-256-GCM-SIV encryption/decryption -pub fn aes_256_gcm_siv_test<A: AesGcmSiv<Key = Aes256Key>>(_marker: marker::PhantomData<A>) { +pub fn aes_256_gcm_siv_test_tc77<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json + // TC77 + let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000"); + let nonce = hex!("030000000000000000000000"); + let aes = A::new(&test_key.into()); + let msg = hex!("0100000000000000"); + let ct = hex!("c2ef328e5c71c83b"); + let tag = hex!("843122130f7364b761e0b97427e3df28"); + let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed"); + assert_eq!(&result[..8], &ct); + assert_eq!(&result[8..], &tag); + assert_eq!(A::TAG_SIZE, result[8..].len()); + let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed"); + assert_eq!(&result[..], &msg); +} + +/// Test AES-256-GCM-SIV encryption/decryption +pub fn aes_256_gcm_siv_test_tc77_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json // TC77 let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000"); @@ -59,22 +154,72 @@ buf.extend_from_slice(&msg); let ct = hex!("c2ef328e5c71c83b"); let tag = hex!("843122130f7364b761e0b97427e3df28"); - assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok()); - assert_eq!(&buf[..8], &ct); - assert_eq!(&buf[8..], &tag); - assert_eq!(A::TAG_SIZE, buf[8..].len()); - assert!(aes.decrypt(&mut buf, b"", &nonce).is_ok()); - assert_eq!(&buf[..], &msg); + let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap(); + assert_eq!(&buf, &ct); + assert_eq!(&actual_tag, &tag); + assert_eq!(A::TAG_SIZE, tag.len()); + assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &actual_tag).is_ok()); + assert_eq!(&buf, &msg); +} + +/// Test AES-256-GCM-SIV encryption/decryption +pub fn aes_256_gcm_siv_test_tc78<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json // TC78 + let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000"); + let nonce = hex!("030000000000000000000000"); + let aes = A::new(&test_key.into()); + let msg = hex!("010000000000000000000000"); + let ct = hex!("9aab2aeb3faa0a34aea8e2b1"); + let tag = hex!("8ca50da9ae6559e48fd10f6e5c9ca17e"); + let result = aes.encrypt(&msg, b"", &nonce).expect("should succeed"); + assert_eq!(&result[..12], &ct); + assert_eq!(&result[12..], &tag); + let result = aes.decrypt(&result[..], b"", &nonce).expect("should succeed"); + assert_eq!(&result[..], &msg); +} + +/// Test AES-256-GCM-SIV encryption/decryption +pub fn aes_256_gcm_siv_test_tc78_detached<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json + // TC78 + let test_key = hex!("0100000000000000000000000000000000000000000000000000000000000000"); + let nonce = hex!("030000000000000000000000"); + let aes = A::new(&test_key.into()); let msg = hex!("010000000000000000000000"); let ct = hex!("9aab2aeb3faa0a34aea8e2b1"); let tag = hex!("8ca50da9ae6559e48fd10f6e5c9ca17e"); let mut buf = Vec::from(msg.as_slice()); - assert!(aes.encrypt(&mut buf, b"", &nonce).is_ok()); - assert_eq!(&buf[..12], &ct); - assert_eq!(&buf[12..], &tag); - assert!(aes.decrypt(&mut buf, b"", &nonce).is_ok()); - assert_eq!(&buf[..], &msg); + let actual_tag = aes.encrypt_detached(&mut buf, b"", &nonce).unwrap(); + assert_eq!(&buf, &ct); + assert_eq!(&actual_tag, &tag); + assert!(aes.decrypt_detached(&mut buf, b"", &nonce, &tag).is_ok()); + assert_eq!(&buf, &msg); +} + +/// Test AES-256-GCM-SIV encryption/decryption where the tag given to decryption doesn't match. +pub fn aes_256_gcm_siv_test_decrypt_detached_bad_tag<A>(_marker: marker::PhantomData<A>) +where + A: AesGcmSiv<Tag = [u8; 16]> + AeadInit<crypto_provider::aes::Aes256Key>, +{ + // https://github.com/google/wycheproof/blob/master/testvectors/aes_gcm_siv_test.json + // TC122 + let test_key = hex!("00112233445566778899aabbccddeeff00112233445566778899aabbccddeeff"); + let nonce = hex!("000000000000000000000000"); + let aes = A::new(&test_key.into()); + let aad = hex!("0289eaa93eb084107d2088435ef2a0cd"); + let ct = hex!("ffffffffffffffff"); + let bad_tag = hex!("ffffffffffffffffffffffffffffffff"); + let mut buf = Vec::from(ct.as_slice()); + aes.decrypt_detached(&mut buf, &aad, &nonce, &bad_tag) + .expect_err("Decrypting with bad tag should fail"); + assert_eq!(&buf, &ct); // The buffer should be unchanged if the decryption failed } /// Generates the test cases to validate the AES-128-GCM-SIV implementation. @@ -93,13 +238,37 @@ #[template] #[export] #[rstest] -#[case::encrypt(aes_128_gcm_siv_test)] -#[case::decrypt(aes_128_gcm_siv_test)] +#[case::encrypt(aes_128_gcm_siv_test_encrypt)] +#[case::decrypt(aes_128_gcm_siv_test_decrypt)] fn aes_128_gcm_siv_test_cases<F: AesGcmSivFactory<Key = Aes128Key>>( #[case] testcase: CryptoProviderTestCase<F>, ) { } +/// Generates the test cases to validate the AES-128-GCM-SIV implementation. +/// For example, to test `MyAesGcmSiv128Impl`: +/// +/// ``` +/// use crypto_provider::aes::aes_gcm_siv::testing::*; +/// +/// mod tests { +/// #[apply(aes_128_gcm_siv_test_cases_detached)] +/// fn aes_128_gcm_siv_tests(testcase: CryptoProviderTestCase<MyAesGcmSivImpl>) { +/// testcase(MyAesGcmSiv128Impl); +/// } +/// } +/// ``` +#[template] +#[export] +#[rstest] +#[case::encrypt_detached(aes_128_gcm_siv_test_encrypt_detached)] +#[case::decrypt_detached(aes_128_gcm_siv_test_decrypt_detached)] +#[case::decrypt_detached_bad_tag(aes_128_gcm_siv_test_decrypt_detached_bad_tag)] +fn aes_128_gcm_siv_test_cases_detached<F: AesGcmSivFactory<Key = Aes128Key>>( + #[case] testcase: CryptoProviderTestCase<F>, +) { +} + /// Generates the test cases to validate the AES-256-GCM-SIV implementation. /// For example, to test `MyAesGcmSiv256Impl`: /// @@ -116,9 +285,33 @@ #[template] #[export] #[rstest] -#[case::encrypt(aes_256_gcm_siv_test)] -#[case::decrypt(aes_256_gcm_siv_test)] +#[case::tc77(aes_256_gcm_siv_test_tc77)] +#[case::tc78(aes_256_gcm_siv_test_tc78)] fn aes_256_gcm_siv_test_cases<F: AesGcmSivFactory<Key = Aes256Key>>( #[case] testcase: CryptoProviderTestCase<F>, ) { } + +/// Generates the test cases to validate the AES-256-GCM-SIV implementation. +/// For example, to test `MyAesGcmSiv256Impl`: +/// +/// ``` +/// use crypto_provider::aes::aes_gcm_siv::testing::*; +/// +/// mod tests { +/// #[apply(aes_256_gcm_siv_test_cases_detached)] +/// fn aes_256_gcm_siv_tests(testcase: CryptoProviderTestCase<MyAesGcmSiv256Impl>) { +/// testcase(MyAesGcmSiv256Impl); +/// } +/// } +/// ``` +#[template] +#[export] +#[rstest] +#[case::tc77_detached(aes_256_gcm_siv_test_tc77_detached)] +#[case::tc78_detached(aes_256_gcm_siv_test_tc78_detached)] +#[case::decrypt_detached_bad_tag(aes_256_gcm_siv_test_decrypt_detached_bad_tag)] +fn aes_256_gcm_siv_test_cases_detached<F: AesGcmSivFactory<Key = Aes256Key>>( + #[case] testcase: CryptoProviderTestCase<F>, +) { +}
diff --git a/nearby/crypto/crypto_provider_test/src/aead/mod.rs b/nearby/crypto/crypto_provider_test/src/aead/mod.rs index 962aa49..3fe857c 100644 --- a/nearby/crypto/crypto_provider_test/src/aead/mod.rs +++ b/nearby/crypto/crypto_provider_test/src/aead/mod.rs
@@ -14,3 +14,6 @@ /// Contains test cases for aes_gcm_siv implementations. pub mod aes_gcm_siv; + +/// Contains test cases for aes_gcm implementations. +pub mod aes_gcm;
diff --git a/nearby/crypto/crypto_provider_test/src/aes/cbc.rs b/nearby/crypto/crypto_provider_test/src/aes/cbc.rs index b22c828..46cbebe 100644 --- a/nearby/crypto/crypto_provider_test/src/aes/cbc.rs +++ b/nearby/crypto/crypto_provider_test/src/aes/cbc.rs
@@ -15,13 +15,17 @@ use crate::aes::Aes256Key; pub use crate::prelude::*; use core::marker::PhantomData; -use crypto_provider::aes::cbc::{AesCbcIv, AesCbcPkcs7Padded}; +use crypto_provider::{ + aes::cbc::{AesCbcIv, AesCbcPkcs7Padded}, + tinyvec::SliceVec, +}; use hex_literal::hex; use rstest_reuse::template; /// Tests for AES-256-CBC encryption pub fn aes_256_cbc_test_encrypt<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) { - // http://google3/third_party/wycheproof/testvectors/aes_cbc_pkcs5_test.json;l=1492;rcl=264817632 + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492 + // tcId: 132 let key: Aes256Key = hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into(); let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb"); @@ -30,9 +34,47 @@ assert_eq!(A::encrypt(&key, &iv, &msg), expected_ciphertext); } +/// Tests for AES-256-CBC in-place encryption +pub fn aes_256_cbc_test_encrypt_in_place<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) { + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492 + // tcId: 132 + let key: Aes256Key = + hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into(); + let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb"); + let msg = hex!("3f56935def3f"); + let expected_ciphertext = hex!("3f3f39697bd7e88d85a14132be1cbc48"); + let mut msg_buffer_backing = [0_u8; 16]; + let mut msg_buffer = SliceVec::from_slice_len(&mut msg_buffer_backing, 0); + msg_buffer.extend_from_slice(&msg); + A::encrypt_in_place(&key, &iv, &mut msg_buffer).unwrap(); + assert_eq!(msg_buffer.as_slice(), &expected_ciphertext); +} + +/// Tests for AES-256-CBC encryption, where the given buffer `SliceVec` is too short to contain the +/// output. +pub fn aes_256_cbc_test_encrypt_in_place_too_short<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) { + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1612 + // tcId: 144 + let key: Aes256Key = + hex!("4f097858a1aec62cf18f0966b2b120783aa4ae9149d3213109740506ae47adfe").into(); + let iv: AesCbcIv = hex!("400aab92803bcbb44a96ef789655b34e"); + let msg = hex!("ee53d8e5039e82d9fcca114e375a014febfea117a7e709d9008d43858e3660"); + let mut msg_buffer_backing = [0_u8; 31]; + let mut msg_buffer = SliceVec::from_slice_len(&mut msg_buffer_backing, 0); + msg_buffer.extend_from_slice(&msg); + A::encrypt_in_place(&key, &iv, &mut msg_buffer) + .expect_err("Encrypting AES with 15-byte buffer should fail"); + // Buffer content is undefined, but test to make sure it doesn't contain half-decrypted data + assert!( + msg_buffer.as_slice() == [0_u8; 32] || msg_buffer.as_slice() == msg, + "Unrecognized content in buffer after decryption failure" + ) +} + /// Tests for AES-256-CBC decryption pub fn aes_256_cbc_test_decrypt<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) { - // http://google3/third_party/wycheproof/testvectors/aes_cbc_pkcs5_test.json;l=1492;rcl=264817632 + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492 + // tcId: 132 let key: Aes256Key = hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into(); let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb"); @@ -41,6 +83,52 @@ assert_eq!(A::decrypt(&key, &iv, &ciphertext).unwrap(), expected_msg); } +/// Tests for AES-256-CBC decryption with bad padding +pub fn aes_256_cbc_test_decrypt_bad_padding<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) { + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1690 + // tcId: 151 + let key: Aes256Key = + hex!("7c78f34dbce8f0557d43630266f59babd1cb92ba624bd1a8f45a2a91c84a804a").into(); + let iv: AesCbcIv = hex!("f010f61c31c9aa8fa0d5be5f6b0f2f70"); + let ciphertext = hex!("8881e9e02fa9e3037b397957ba1fb7ce64679a46621b792f643542a735f0bbbf"); + A::decrypt(&key, &iv, &ciphertext).expect_err("Decryption with bad padding should fail"); +} + +/// Tests for AES-256-CBC in-place decryption +pub fn aes_256_cbc_test_decrypt_in_place<A: AesCbcPkcs7Padded>(_marker: PhantomData<A>) { + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1492 + // tcId: 132 + let key: Aes256Key = + hex!("665a02bc265a66d01775091da56726b6668bfd903cb7af66fb1b78a8a062e43c").into(); + let iv: AesCbcIv = hex!("3fb0d5ecd06c71150748b599595833cb"); + let mut ciphertext = hex!("3f3f39697bd7e88d85a14132be1cbc48"); + let expected_msg = hex!("3f56935def3f"); + let mut msg_buffer = SliceVec::from(&mut ciphertext); + A::decrypt_in_place(&key, &iv, &mut msg_buffer).unwrap(); + assert_eq!(msg_buffer.as_slice(), expected_msg); +} + +/// Tests for AES-256-CBC in-place decryption with bad padding +pub fn aes_256_cbc_test_decrypt_in_place_bad_padding<A: AesCbcPkcs7Padded>( + _marker: PhantomData<A>, +) { + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/aes_cbc_pkcs5_test.json#L1690 + // tcId: 151 + let key: Aes256Key = + hex!("7c78f34dbce8f0557d43630266f59babd1cb92ba624bd1a8f45a2a91c84a804a").into(); + let iv: AesCbcIv = hex!("f010f61c31c9aa8fa0d5be5f6b0f2f70"); + let ciphertext = hex!("8881e9e02fa9e3037b397957ba1fb7ce64679a46621b792f643542a735f0bbbf"); + let mut msg_buffer_backing = ciphertext; + let mut msg_buffer = SliceVec::from(&mut msg_buffer_backing); + A::decrypt_in_place(&key, &iv, &mut msg_buffer) + .expect_err("Decryption with bad padding should fail"); + // Buffer content is undefined, but test to make sure it doesn't contain half-decrypted data + assert!( + msg_buffer.as_slice() == [0_u8; 32] || msg_buffer.as_slice() == ciphertext, + "Unrecognized content in buffer after decryption failure" + ) +} + /// Generates the test cases to validate the AES-256-CBC implementation. /// For example, to test `MyAesCbc256Impl`: /// @@ -59,5 +147,9 @@ #[export] #[rstest] #[case::encrypt(aes_256_cbc_test_encrypt)] +#[case::encrypt_in_place(aes_256_cbc_test_encrypt_in_place)] +#[case::encrypt_in_place_too_short(aes_256_cbc_test_encrypt_in_place_too_short)] #[case::decrypt(aes_256_cbc_test_decrypt)] +#[case::decrypt_bad_padding(aes_256_cbc_test_decrypt_bad_padding)] +#[case::decrypt_in_place_bad_padding(aes_256_cbc_test_decrypt_in_place_bad_padding)] fn aes_256_cbc_test_cases<A: AesCbcPkcs7Padded>(#[case] testcase: CryptoProviderTestCases<F>) {}
diff --git a/nearby/crypto/crypto_provider_test/src/aes/ctr.rs b/nearby/crypto/crypto_provider_test/src/aes/ctr.rs index b1d6b8b..de71740 100644 --- a/nearby/crypto/crypto_provider_test/src/aes/ctr.rs +++ b/nearby/crypto/crypto_provider_test/src/aes/ctr.rs
@@ -27,22 +27,22 @@ let mut cipher = A::new(&key, NonceAndCounter::from_block(iv)); block = hex!("6bc1bee22e409f96e93d7e117393172a"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_1 = hex!("874d6191b620e3261bef6864990db6ce"); assert_eq!(expected_ciphertext_1, block); block = hex!("ae2d8a571e03ac9c9eb76fac45af8e51"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_2 = hex!("9806f66b7970fdff8617187bb9fffdff"); assert_eq!(expected_ciphertext_2, block); block = hex!("30c81c46a35ce411e5fbc1191a0a52ef"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_3 = hex!("5ae4df3edbd5d35e5b4f09020db03eab"); assert_eq!(expected_ciphertext_3, block); block = hex!("f69f2445df4f9b17ad2b417be66c3710"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_3 = hex!("1e031dda2fbe03d1792170a0f3009cee"); assert_eq!(expected_ciphertext_3, block); } @@ -56,22 +56,22 @@ let mut cipher = A::new(&key, NonceAndCounter::from_block(iv)); block = hex!("874d6191b620e3261bef6864990db6ce"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_1 = hex!("6bc1bee22e409f96e93d7e117393172a"); assert_eq!(expected_plaintext_1, block); block = hex!("9806f66b7970fdff8617187bb9fffdff"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_2 = hex!("ae2d8a571e03ac9c9eb76fac45af8e51"); assert_eq!(expected_plaintext_2, block); block = hex!("5ae4df3edbd5d35e5b4f09020db03eab"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_3 = hex!("30c81c46a35ce411e5fbc1191a0a52ef"); assert_eq!(expected_plaintext_3, block); block = hex!("1e031dda2fbe03d1792170a0f3009cee"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_3 = hex!("f69f2445df4f9b17ad2b417be66c3710"); assert_eq!(expected_plaintext_3, block); } @@ -86,22 +86,22 @@ let mut cipher = A::new(&key, NonceAndCounter::from_block(iv)); block = hex!("6bc1bee22e409f96e93d7e117393172a"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_1 = hex!("601ec313775789a5b7a7f504bbf3d228"); assert_eq!(expected_ciphertext_1, block); block = hex!("ae2d8a571e03ac9c9eb76fac45af8e51"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_2 = hex!("f443e3ca4d62b59aca84e990cacaf5c5"); assert_eq!(expected_ciphertext_2, block); block = hex!("30c81c46a35ce411e5fbc1191a0a52ef"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_3 = hex!("2b0930daa23de94ce87017ba2d84988d"); assert_eq!(expected_ciphertext_3, block); block = hex!("f69f2445df4f9b17ad2b417be66c3710"); - cipher.encrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_ciphertext_3 = hex!("dfc9c58db67aada613c2dd08457941a6"); assert_eq!(expected_ciphertext_3, block); } @@ -116,22 +116,22 @@ let mut cipher = A::new(&key, NonceAndCounter::from_block(iv)); block = hex!("601ec313775789a5b7a7f504bbf3d228"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_1 = hex!("6bc1bee22e409f96e93d7e117393172a"); assert_eq!(expected_plaintext_1, block); block = hex!("f443e3ca4d62b59aca84e990cacaf5c5"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_2 = hex!("ae2d8a571e03ac9c9eb76fac45af8e51"); assert_eq!(expected_plaintext_2, block); block = hex!("2b0930daa23de94ce87017ba2d84988d"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_3 = hex!("30c81c46a35ce411e5fbc1191a0a52ef"); assert_eq!(expected_plaintext_3, block); block = hex!("dfc9c58db67aada613c2dd08457941a6"); - cipher.decrypt(&mut block); + cipher.apply_keystream(&mut block); let expected_plaintext_3 = hex!("f69f2445df4f9b17ad2b417be66c3710"); assert_eq!(expected_plaintext_3, block); }
diff --git a/nearby/crypto/crypto_provider_test/src/ed25519.rs b/nearby/crypto/crypto_provider_test/src/ed25519.rs index d99605c..0fdb484 100644 --- a/nearby/crypto/crypto_provider_test/src/ed25519.rs +++ b/nearby/crypto/crypto_provider_test/src/ed25519.rs
@@ -18,7 +18,9 @@ use alloc::borrow::ToOwned; use alloc::string::String; use alloc::vec::Vec; -use crypto_provider::ed25519::{Ed25519Provider, KeyPair, PublicKey, Signature}; +use crypto_provider::ed25519::{ + Ed25519Provider, KeyPair, PublicKey, RawPrivateKeyPermit, RawSignature, Signature, +}; use wycheproof::TestResult; // These are test vectors from the creators of Ed25519: https://ed25519.cr.yp.to/ which are referenced @@ -27,7 +29,7 @@ // also used for test cases in the above RFC: // https://dev.gnupg.org/source/libgcrypt/browse/master/tests/t-ed25519.inp const PATH_TO_RFC_VECTORS_FILE: &str = - "crypto/crypto_provider_test/src/testdata/ecdsa/rfc_test_vectors.txt"; + "crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt"; /// Runs set of Ed25519 wycheproof test vectors against a provided ed25519 implementation /// Tests vectors from Project Wycheproof: <https://github.com/google/wycheproof> @@ -39,10 +41,7 @@ .expect("should be able to load test set"); for test_group in test_set.test_groups { - let key_pair = test_group.key; - let public_key = key_pair.pk; - let secret_key = key_pair.sk; - + let public_key = test_group.key.pk.to_vec(); for test in test_group.tests { let tc_id = test.tc_id; let comment = test.comment; @@ -53,8 +52,7 @@ TestResult::Invalid => false, TestResult::Valid | TestResult::Acceptable => true, }; - let result = - run_test::<E>(public_key.clone(), secret_key.clone(), sig.clone(), msg.clone()); + let result = run_wycheproof_test::<E>(public_key.to_vec(), sig.to_vec(), msg.to_vec()); if valid { if let Err(desc) = result { panic!( @@ -73,6 +71,20 @@ } } +fn run_wycheproof_test<E>(pub_key: Vec<u8>, sig: Vec<u8>, msg: Vec<u8>) -> Result<(), &'static str> +where + E: Ed25519Provider, +{ + let pub_key = E::PublicKey::from_bytes(pub_key.as_slice().try_into().unwrap()) + .map_err(|_| "Invalid public key bytes")?; + + let raw_sig: RawSignature = + sig.as_slice().try_into().map_err(|_| "Invalid length signature")?; + let signature = E::Signature::from_bytes(&raw_sig); + + pub_key.verify_strict(msg.as_slice(), &signature).map_err(|_| "Signature verification failed") +} + /// Runs the RFC specified test vectors against an Ed25519 implementation pub fn run_rfc_test_vectors<E>() where @@ -126,7 +138,10 @@ { let private_key_bytes: [u8; 32] = private_key.as_slice().try_into().expect("Secret key is the wrong length"); - let kp = E::KeyPair::from_private_key(&private_key_bytes); + + // Permits: Test-only code, not a production leak of the private key + let permit = RawPrivateKeyPermit::default(); + let kp = E::KeyPair::from_raw_private_key(&private_key_bytes, &permit); let sig_result = kp.sign(msg.as_slice()); (sig.as_slice() == sig_result.to_bytes()).then_some(()).ok_or("sig not matching expected")?;
diff --git a/nearby/crypto/crypto_provider_test/src/lib.rs b/nearby/crypto/crypto_provider_test/src/lib.rs index 8e63f44..2209a3e 100644 --- a/nearby/crypto/crypto_provider_test/src/lib.rs +++ b/nearby/crypto/crypto_provider_test/src/lib.rs
@@ -36,9 +36,11 @@ /// Common items that needs to be imported to use these test cases pub mod prelude { pub use super::CryptoProviderTestCase; + pub use ::rstest; pub use rstest::rstest; pub use rstest_reuse; pub use rstest_reuse::apply; + pub extern crate std; } /// A test case for Crypto Provider. A test case is a function that panics if the test fails.
diff --git a/nearby/crypto/crypto_provider_test/src/p256.rs b/nearby/crypto/crypto_provider_test/src/p256.rs index 9869c9e..5ebbd50 100644 --- a/nearby/crypto/crypto_provider_test/src/p256.rs +++ b/nearby/crypto/crypto_provider_test/src/p256.rs
@@ -12,12 +12,12 @@ // See the License for the specific language governing permissions and // limitations under the License. -extern crate std; use crate::elliptic_curve::EphemeralSecretForTesting; pub use crate::prelude::*; use crate::TestError; use core::marker::PhantomData; -use crypto_provider::p256::{P256PublicKey, P256}; +use core::ops::Deref; +use crypto_provider::p256::{P256PublicKey, PointCompression, P256}; use crypto_provider::{ elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey}, CryptoRng, @@ -62,9 +62,35 @@ 8bbe76c6dc1643088107636deff8aa79e8002a157b92" ); let key = E::PublicKey::from_sec1_bytes(&sec1_bytes).unwrap(); + // Not part of the API contract, but `to_bytes()` should prefer to use uncompressed + // representation since support for compressed point is optional. + let key_bytes = key.to_bytes(); + assert_eq!(sec1_bytes.to_vec(), key_bytes.deref()); +} + +/// Test for P256PublicKey::to_sec1_bytes(Compressed). Support for compressed representation is +/// optional. +pub fn to_bytes_compressed_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { + let sec1_bytes = hex!( + "04756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09f0b6da270d2a58a06022 + 8bbe76c6dc1643088107636deff8aa79e8002a157b92" + ); + let key = E::PublicKey::from_sec1_bytes(&sec1_bytes).unwrap(); + let key_bytes = key.to_sec1_bytes(PointCompression::Compressed); let sec1_bytes_compressed = hex!("02756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09"); - assert_eq!(sec1_bytes_compressed.to_vec(), key.to_bytes()); + assert_eq!(sec1_bytes_compressed.to_vec(), key_bytes.deref()); +} + +/// Test for P256PublicKey::to_sec1_bytes(Uncompressed) +pub fn to_bytes_uncompressed_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { + let sec1_bytes = hex!( + "04756c07ba5b596fa96c9099e6619dc62deac4297a8fc1d803d74dc5caa9197c09f0b6da270d2a58a06022 + 8bbe76c6dc1643088107636deff8aa79e8002a157b92" + ); + let key = E::PublicKey::from_sec1_bytes(&sec1_bytes).unwrap(); + let key_bytes = key.to_sec1_bytes(PointCompression::Uncompressed); + assert_eq!(sec1_bytes.to_vec(), key_bytes.deref()); } /// Random test for P256PublicKey::to_bytes @@ -75,7 +101,7 @@ P256, >>::Rng::new()) .public_key_bytes(); - let public_key = E::PublicKey::from_bytes(&public_key_bytes).unwrap(); + let public_key = E::PublicKey::from_bytes(public_key_bytes.as_ref()).unwrap(); assert_eq!( E::PublicKey::from_bytes(&public_key.to_bytes()).unwrap(), public_key, @@ -192,7 +218,7 @@ /// Test for P256 Diffie-Hellman key exchange. pub fn p256_ecdh_test<E: EcdhProviderForP256Test>(_: PhantomData<E>) { // From wycheproof ecdh_secp256r1_ecpoint_test.json, tcId 1 - // http://google3/third_party/wycheproof/testvectors/ecdh_secp256r1_ecpoint_test.json;l=22;rcl=375894991 + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/ecdh_secp256r1_ecpoint_test.json#L22 // sec1 public key manually extracted from the ASN encoded test data let public_key_sec1 = hex!( "0462d5bd3372af75fe85a040715d0f502428e07046868b0bfdfa61d731afe44f @@ -230,20 +256,24 @@ }; let result = p256_ecdh_test_impl::<E>( &test.public_key, - &test.private_key.try_into().expect("Private key should be 32 bytes long"), + &test + .private_key + .as_slice() + .try_into() + .expect("Private key should be 32 bytes long"), ); match test.result { wycheproof::TestResult::Valid => { let shared_secret = result.unwrap_or_else(|_| panic!("Test {} should succeed", test.tc_id)); - assert_eq!(test.shared_secret, shared_secret.into()); + assert_eq!(test.shared_secret.as_slice(), shared_secret.into()); } wycheproof::TestResult::Invalid => { result.err().unwrap_or_else(|| panic!("Test {} should fail", test.tc_id)); } wycheproof::TestResult::Acceptable => { if let Ok(shared_secret) = result { - assert_eq!(test.shared_secret, shared_secret.into()); + assert_eq!(test.shared_secret.as_slice(), shared_secret.into()); } // Test passes if `result` is an error because this test is "acceptable" } @@ -268,22 +298,43 @@ #[template] #[export] #[rstest] -#[case::to_bytes(to_bytes_test)] -#[case::to_bytes_random(to_bytes_random_test)] -#[case::from_sec1_bytes_not_on_curve(from_sec1_bytes_not_on_curve_test)] -#[case::from_sec1_bytes_not_on_infinity(from_sec1_bytes_at_infinity_test)] -#[case::from_affine_coordinates(from_affine_coordinates_test)] -#[case::from_affine_coordinates_not_on_curve(from_affine_coordinates_not_on_curve_test)] -#[case::public_key_to_affine_coordinates(public_key_to_affine_coordinates_test)] +#[case::to_bytes(to_bytes_test, "to_bytes")] +#[case::to_bytes_compressed(to_bytes_compressed_test, "to_bytes_compressed")] +#[case::to_bytes_uncompressed(to_bytes_uncompressed_test, "to_bytes_uncompressed")] +#[case::to_bytes_random(to_bytes_random_test, "to_bytes_random")] +#[case::from_sec1_bytes_not_on_curve( + from_sec1_bytes_not_on_curve_test, + "from_sec1_bytes_not_on_curve" +)] +#[case::from_sec1_bytes_not_on_infinity( + from_sec1_bytes_at_infinity_test, + "from_sec1_bytes_not_on_infinity" +)] +#[case::from_affine_coordinates(from_affine_coordinates_test, "from_affine_coordinates")] +#[case::from_affine_coordinates_not_on_curve( + from_affine_coordinates_not_on_curve_test, + "from_affine_coordinates_not_on_curve" +)] +#[case::public_key_to_affine_coordinates( + public_key_to_affine_coordinates_test, + "public_key_to_affine_coordinates" +)] #[case::public_key_to_affine_coordinates_compressed02( - public_key_to_affine_coordinates_compressed02_test + public_key_to_affine_coordinates_compressed02_test, + "public_key_to_affine_coordinates_compressed02" )] #[case::public_key_to_affine_coordinates_compressed03( - public_key_to_affine_coordinates_compressed03_test + public_key_to_affine_coordinates_compressed03_test, + "public_key_to_affine_coordinates_compressed03" )] #[case::public_key_to_affine_coordinates_zero_top_byte( - public_key_to_affine_coordinates_zero_top_byte_test + public_key_to_affine_coordinates_zero_top_byte_test, + "public_key_to_affine_coordinates_zero_top_byte" )] -#[case::p256_ecdh(p256_ecdh_test)] -#[case::wycheproof_p256(wycheproof_p256_test)] -fn p256_test_cases<C: CryptoProvider>(#[case] testcase: CryptoProviderTestCase<C>) {} +#[case::p256_ecdh(p256_ecdh_test, "p256_ecdh")] +#[case::wycheproof_p256(wycheproof_p256_test, "wycheproof_p256")] +fn p256_test_cases<C: CryptoProvider>( + #[case] testcase: CryptoProviderTestCase<C>, + #[case] name: &str, +) { +}
diff --git a/nearby/crypto/crypto_provider_test/src/sha2.rs b/nearby/crypto/crypto_provider_test/src/sha2.rs index 330f284..66a3025 100644 --- a/nearby/crypto/crypto_provider_test/src/sha2.rs +++ b/nearby/crypto/crypto_provider_test/src/sha2.rs
@@ -14,7 +14,7 @@ extern crate alloc; extern crate std; -pub use crate::prelude::*; +use crate::prelude::*; use crate::CryptoProvider; use alloc::vec::Vec; use core::{marker::PhantomData, str::FromStr};
diff --git a/nearby/crypto/crypto_provider_test/src/testdata/ecdsa/rfc_test_vectors.txt b/nearby/crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt similarity index 100% rename from nearby/crypto/crypto_provider_test/src/testdata/ecdsa/rfc_test_vectors.txt rename to nearby/crypto/crypto_provider_test/src/testdata/EdDSA/rfc_test_vectors.txt
diff --git a/nearby/crypto/crypto_provider_test/src/x25519.rs b/nearby/crypto/crypto_provider_test/src/x25519.rs index 0fcaa12..1fe2019 100644 --- a/nearby/crypto/crypto_provider_test/src/x25519.rs +++ b/nearby/crypto/crypto_provider_test/src/x25519.rs
@@ -58,7 +58,7 @@ pub fn x25519_to_bytes_test<E: EcdhProviderForX25519Test>(_: PhantomData<E>) { let public_key_bytes = hex!("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829"); let public_key = E::PublicKey::from_bytes(&public_key_bytes).unwrap(); - assert_eq!(public_key_bytes.to_vec(), public_key.to_bytes()); + assert_eq!(public_key_bytes.to_vec(), public_key.to_bytes().as_ref()); } /// Random test for `PublicKey<X25519>::to_bytes` @@ -69,9 +69,9 @@ X25519, >>::Rng::new()) .public_key_bytes(); - let public_key = E::PublicKey::from_bytes(&public_key_bytes).unwrap(); + let public_key = E::PublicKey::from_bytes(public_key_bytes.as_ref()).unwrap(); assert_eq!( - E::PublicKey::from_bytes(&public_key.to_bytes()).unwrap(), + E::PublicKey::from_bytes(public_key.to_bytes().as_ref()).unwrap(), public_key, "from_bytes should return the same key for `{public_key_bytes:?}`", ); @@ -81,7 +81,7 @@ /// Test for X25519 Diffie-Hellman key exchange. pub fn x25519_ecdh_test<E: EcdhProviderForX25519Test>(_: PhantomData<E>) { // From wycheproof ecdh_secx25519r1_ecpoint_test.json, tcId 1 - // http://google3/third_party/wycheproof/testvectors/ecdh_secx25519r1_ecpoint_test.json;l=22;rcl=375894991 + // https://github.com/google/wycheproof/blob/b063b4a/testvectors/x25519_test.json#L23 // sec1 public key manually extracted from the ASN encoded test data let public_key = hex!("504a36999f489cd2fdbc08baff3d88fa00569ba986cba22548ffde80f9806829"); let private = hex!("c8a9d5a91091ad851c668b0736c1c9a02936c0d3ad62670858088047ba057475"); @@ -109,20 +109,24 @@ for test in test_group.tests { let result = x25519_ecdh_test_impl::<E>( &test.public_key, - &test.private_key.try_into().expect("Private keys should be 32 bytes long"), + &test + .private_key + .as_slice() + .try_into() + .expect("Private keys should be 32 bytes long"), ); match test.result { wycheproof::TestResult::Valid => { let shared_secret = result.unwrap_or_else(|_| panic!("Test {} should succeed", test.tc_id)); - assert_eq!(&test.shared_secret, &shared_secret.into()); + assert_eq!(&test.shared_secret.as_slice(), &shared_secret.into()); } wycheproof::TestResult::Invalid => { result.err().unwrap_or_else(|| panic!("Test {} should fail", test.tc_id)); } wycheproof::TestResult::Acceptable => { if let Ok(shared_secret) = result { - assert_eq!(test.shared_secret, shared_secret.into()); + assert_eq!(test.shared_secret.as_slice(), shared_secret.into()); } // Test passes if `result` is an error because this test is "acceptable" }