Project import generated by Copybara.
GitOrigin-RevId: 8e45d682af0c4329d479cb7a1e0b83d70c9a94f3
Change-Id: I81aa3256a4bd578d47e2743b4e67c74cb24823f9
diff --git a/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
new file mode 100644
index 0000000..ad5a4b8
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/Cargo.toml
@@ -0,0 +1,37 @@
+[package]
+name = "crypto_provider_rustcrypto"
+version.workspace = true
+edition.workspace = true
+publish.workspace = true
+
+[dependencies]
+aead = "0.5.1"
+aes-gcm-siv = { version = "0.11.1", features = ["aes"], optional = true }
+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 }
+rand = { workspace = true, default-features = false }
+rand_core_05_adapter.workspace = true
+rand_core.workspace = true
+subtle.workspace = true
+aes.workspace = true
+ctr.workspace = true
+cbc.workspace = true
+cfg-if.workspace = true
+rand_chacha = { workspace = true, default-features = false, optional = true }
+
+[dev-dependencies]
+hex.workspace = true
+crypto_provider = { workspace = true, features = ["testing"] }
+crypto_provider_rustcrypto = { path = ".", features = ["std"] }
+
+[features]
+default = ["alloc", "gcm_siv", "rand_chacha"]
+std = ["ed25519-dalek/default", "rand/std", "rand/std_rng", "crypto_provider/std", "crypto_provider/alloc"]
+alloc = ["aead/bytes"]
+gcm_siv = ["crypto_provider/gcm_siv", "dep:aes-gcm-siv"]
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs
new file mode 100644
index 0000000..b43876c
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes/cbc.rs
@@ -0,0 +1,53 @@
+// 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::aes::cbc::testing::*;
+
+ #[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/gcm_siv.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes/gcm_siv.rs
new file mode 100644
index 0000000..d5c655b
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes/gcm_siv.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 aes_gcm_siv::{AeadInPlace, Aes128GcmSiv, Aes256GcmSiv, KeyInit, Nonce};
+
+use crypto_provider::aes::gcm_siv::{bytes, GcmSivError};
+use crypto_provider::aes::{Aes128Key, Aes256Key, AesKey};
+
+pub struct AesGcmSiv128(Aes128GcmSiv);
+
+impl crypto_provider::aes::gcm_siv::AesGcmSiv for AesGcmSiv128 {
+ type Key = Aes128Key;
+
+ fn new(key: &Self::Key) -> Self {
+ Self(Aes128GcmSiv::new(key.as_slice().into()))
+ }
+
+ fn encrypt(
+ &self,
+ data: &mut bytes::BytesMut,
+ aad: &[u8],
+ nonce: &[u8],
+ ) -> Result<(), GcmSivError> {
+ self.0
+ .encrypt_in_place(Nonce::from_slice(nonce), aad, data)
+ .map_err(|_| GcmSivError::EncryptOutBufferTooSmall)
+ }
+
+ fn decrypt(
+ &self,
+ data: &mut bytes::BytesMut,
+ aad: &[u8],
+ nonce: &[u8],
+ ) -> Result<(), GcmSivError> {
+ self.0
+ .decrypt_in_place(Nonce::from_slice(nonce), aad, data)
+ .map_err(|_| GcmSivError::DecryptTagDoesNotMatch)
+ }
+}
+
+pub struct AesGcmSiv256(Aes256GcmSiv);
+
+impl crypto_provider::aes::gcm_siv::AesGcmSiv for AesGcmSiv256 {
+ type Key = Aes256Key;
+
+ fn new(key: &Self::Key) -> Self {
+ Self(Aes256GcmSiv::new(key.as_slice().into()))
+ }
+
+ fn encrypt(
+ &self,
+ data: &mut bytes::BytesMut,
+ aad: &[u8],
+ nonce: &[u8],
+ ) -> Result<(), GcmSivError> {
+ self.0
+ .encrypt_in_place(Nonce::from_slice(nonce), aad, data)
+ .map_err(|_| GcmSivError::EncryptOutBufferTooSmall)
+ }
+
+ fn decrypt(
+ &self,
+ data: &mut bytes::BytesMut,
+ aad: &[u8],
+ nonce: &[u8],
+ ) -> Result<(), GcmSivError> {
+ self.0
+ .decrypt_in_place(Nonce::from_slice(nonce), aad, data)
+ .map_err(|_| GcmSivError::DecryptTagDoesNotMatch)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::marker::PhantomData;
+
+ use crypto_provider::aes::gcm_siv::testing::*;
+ use crypto_provider::aes::testing::*;
+
+ use super::*;
+
+ #[apply(aes_128_gcm_siv_test_cases)]
+ fn aes_gcm_siv_128_test(testcase: CryptoProviderTestCase<AesGcmSiv128>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_gcm_siv_test_cases)]
+ fn aes_gcm_siv_256_test(testcase: CryptoProviderTestCase<AesGcmSiv256>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs b/nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs
new file mode 100644
index 0000000..a351b81
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/aes/mod.rs
@@ -0,0 +1,191 @@
+// 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.
+
+//! Implementation of `crypto_provider::aes` types using RustCrypto's `aes`.
+#![forbid(unsafe_code)]
+
+use aes::cipher::{
+ generic_array, BlockDecrypt as _, BlockEncrypt as _, KeyInit as _, KeyIvInit as _,
+ StreamCipher as _,
+};
+
+use crypto_provider::aes::{
+ Aes, Aes128Key, Aes256Key, AesBlock, AesCipher, AesDecryptCipher, AesEncryptCipher, AesKey,
+};
+
+/// Module implementing AES-CBC.
+#[cfg(feature = "alloc")]
+pub(crate) mod cbc;
+#[cfg(feature = "gcm_siv")]
+pub(crate) mod gcm_siv;
+
+/// Rust crypto implementation of AES-128
+pub struct Aes128;
+impl Aes for Aes128 {
+ type Key = Aes128Key;
+ type EncryptCipher = Aes128Cipher;
+ type DecryptCipher = Aes128Cipher;
+}
+
+/// Rust crypto implementation of AES-256
+pub struct Aes256;
+impl Aes for Aes256 {
+ type Key = Aes256Key;
+ type EncryptCipher = Aes256Cipher;
+ type DecryptCipher = Aes256Cipher;
+}
+
+/// A Rust Crypto AES-128 cipher used for encryption and decryption
+pub struct Aes128Cipher(aes::Aes128);
+
+impl AesCipher for Aes128Cipher {
+ type Key = Aes128Key;
+
+ fn new(key: &Self::Key) -> Self {
+ Self(aes::Aes128::new(key.as_array().into()))
+ }
+}
+
+impl AesEncryptCipher for Aes128Cipher {
+ fn encrypt(&self, block: &mut AesBlock) {
+ self.0
+ .encrypt_block(generic_array::GenericArray::from_mut_slice(
+ block.as_mut_slice(),
+ ));
+ }
+}
+
+impl AesDecryptCipher for Aes128Cipher {
+ fn decrypt(&self, block: &mut AesBlock) {
+ self.0
+ .decrypt_block(generic_array::GenericArray::from_mut_slice(
+ block.as_mut_slice(),
+ ))
+ }
+}
+
+/// A Rust Crypto AES-256 cipher used for encryption and decryption
+pub struct Aes256Cipher(aes::Aes256);
+
+impl AesCipher for Aes256Cipher {
+ type Key = Aes256Key;
+
+ fn new(key: &Self::Key) -> Self {
+ Self(aes::Aes256::new(key.as_array().into()))
+ }
+}
+
+impl AesEncryptCipher for Aes256Cipher {
+ fn encrypt(&self, block: &mut AesBlock) {
+ self.0
+ .encrypt_block(generic_array::GenericArray::from_mut_slice(
+ block.as_mut_slice(),
+ ));
+ }
+}
+
+impl AesDecryptCipher for Aes256Cipher {
+ fn decrypt(&self, block: &mut AesBlock) {
+ self.0
+ .decrypt_block(generic_array::GenericArray::from_mut_slice(
+ block.as_mut_slice(),
+ ))
+ }
+}
+
+/// RustCrypto implementation of AES-CTR 128.
+pub struct AesCtr128 {
+ cipher: ctr::Ctr128BE<aes::Aes128>,
+}
+
+impl crypto_provider::aes::ctr::AesCtr for AesCtr128 {
+ type Key = crypto_provider::aes::Aes128Key;
+
+ fn new(key: &Self::Key, iv: [u8; 16]) -> Self {
+ Self {
+ cipher: ctr::Ctr128BE::new(key.as_array().into(), &iv.into()),
+ }
+ }
+
+ fn encrypt(&mut self, data: &mut [u8]) {
+ self.cipher.apply_keystream(data);
+ }
+
+ fn decrypt(&mut self, data: &mut [u8]) {
+ self.cipher.apply_keystream(data);
+ }
+}
+
+/// RustCrypto implementation of AES-CTR 256.
+pub struct AesCtr256 {
+ cipher: ctr::Ctr128BE<aes::Aes256>,
+}
+
+impl crypto_provider::aes::ctr::AesCtr for AesCtr256 {
+ type Key = crypto_provider::aes::Aes256Key;
+
+ fn new(key: &Self::Key, iv: [u8; 16]) -> Self {
+ Self {
+ cipher: ctr::Ctr128BE::new(key.as_array().into(), &iv.into()),
+ }
+ }
+
+ fn encrypt(&mut self, data: &mut [u8]) {
+ self.cipher.apply_keystream(data);
+ }
+
+ fn decrypt(&mut self, data: &mut [u8]) {
+ self.cipher.apply_keystream(data);
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use core::marker::PhantomData;
+
+ use crypto_provider::aes::ctr::testing::*;
+ use crypto_provider::aes::testing::*;
+
+ use super::*;
+
+ #[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);
+ }
+
+ #[apply(aes_128_encrypt_test_cases)]
+ fn aes_128_encrypt_test(testcase: CryptoProviderTestCase<Aes128Cipher>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_128_decrypt_test_cases)]
+ fn aes_128_decrypt_test(testcase: CryptoProviderTestCase<Aes128Cipher>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_encrypt_test_cases)]
+ fn aes_256_encrypt_test(testcase: CryptoProviderTestCase<Aes256Cipher>) {
+ testcase(PhantomData);
+ }
+
+ #[apply(aes_256_decrypt_test_cases)]
+ fn aes_256_decrypt_test(testcase: CryptoProviderTestCase<Aes256Cipher>) {
+ testcase(PhantomData);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
new file mode 100644
index 0000000..874aa82
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/ed25519.rs
@@ -0,0 +1,122 @@
+// 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::ed25519::{
+ InvalidBytes, InvalidSignature, Signature as _, SignatureError, KEY_LENGTH, KEY_PAIR_LENGTH,
+ SIGNATURE_LENGTH,
+};
+use ed25519_dalek::Signer;
+
+pub struct Ed25519;
+impl crypto_provider::ed25519::Ed25519Provider for Ed25519 {
+ type KeyPair = KeyPair;
+ type PublicKey = PublicKey;
+ type Signature = Signature;
+}
+
+pub struct KeyPair(ed25519_dalek::Keypair);
+impl crypto_provider::ed25519::KeyPair for KeyPair {
+ type PublicKey = PublicKey;
+ type Signature = Signature;
+
+ fn to_bytes(&self) -> [u8; KEY_PAIR_LENGTH] {
+ self.0.to_bytes()
+ }
+
+ fn from_bytes(bytes: [u8; KEY_PAIR_LENGTH]) -> Result<Self, InvalidBytes> {
+ ed25519_dalek::Keypair::from_bytes(&bytes)
+ .map(Self)
+ .map_err(|_| InvalidBytes)
+ }
+
+ #[allow(clippy::expect_used)]
+ fn sign(&self, msg: &[u8]) -> Self::Signature {
+ Self::Signature::from_bytes(&self.0.sign(msg).to_bytes())
+ .expect("a signature will always produce valid bytes for creating a Signature")
+ }
+
+ //TODO: allow providing a crypto rng and make it a no-op for openssl if the need arises to
+ // improve perf of keypair generation
+ #[cfg(feature = "std")]
+ fn generate() -> Self {
+ let mut csprng = rand::rngs::ThreadRng::default();
+ Self(ed25519_dalek::Keypair::generate(
+ &mut rand_core_05_adapter::RandWrapper::from(&mut csprng),
+ ))
+ }
+
+ fn public(&self) -> Self::PublicKey {
+ PublicKey(self.0.public)
+ }
+}
+
+pub struct Signature(ed25519_dalek::Signature);
+impl crypto_provider::ed25519::Signature for Signature {
+ fn from_bytes(bytes: &[u8]) -> Result<Self, InvalidSignature> {
+ if bytes.len() != SIGNATURE_LENGTH {
+ return Err(InvalidSignature);
+ }
+ ed25519_dalek::Signature::from_bytes(bytes)
+ .map(Self)
+ .map_err(|_| InvalidSignature)
+ }
+
+ fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
+ self.0.to_bytes()
+ }
+}
+
+pub struct PublicKey(ed25519_dalek::PublicKey);
+impl crypto_provider::ed25519::PublicKey for PublicKey {
+ type Signature = Signature;
+
+ fn from_bytes(bytes: [u8; KEY_LENGTH]) -> Result<Self, InvalidBytes>
+ where
+ Self: Sized,
+ {
+ ed25519_dalek::PublicKey::from_bytes(&bytes)
+ .map(PublicKey)
+ .map_err(|_| InvalidBytes)
+ }
+
+ fn to_bytes(&self) -> [u8; KEY_LENGTH] {
+ self.0.to_bytes()
+ }
+
+ fn verify_strict(
+ &self,
+ message: &[u8],
+ signature: &Self::Signature,
+ ) -> Result<(), SignatureError> {
+ self.0
+ .verify_strict(message, &signature.0)
+ .map_err(|_| SignatureError)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::ed25519::Ed25519;
+ use crypto_provider::ed25519::testing::{run_rfc_test_vectors, run_wycheproof_test_vectors};
+
+ #[test]
+ fn wycheproof_test_ed25519_rustcrypto() {
+ run_wycheproof_test_vectors::<Ed25519>()
+ }
+
+ #[test]
+ fn rfc_test_ed25519_rustcrypto() {
+ run_rfc_test_vectors::<Ed25519>()
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.rs
new file mode 100644
index 0000000..d6ca687
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/hkdf_rc.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 crypto_provider::hkdf::InvalidLength;
+use hmac::digest::block_buffer::Eager;
+use hmac::digest::consts::U256;
+use hmac::digest::core_api::{
+ BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore,
+};
+use hmac::digest::typenum::{IsLess, Le, NonZero};
+use hmac::digest::{HashMarker, OutputSizeUser};
+
+/// RustCrypto based hkdf implementation
+#[derive(Clone)]
+pub struct Hkdf<D>
+where
+ D: OutputSizeUser,
+ D: CoreProxy,
+ D::Core: HashMarker
+ + UpdateCore
+ + FixedOutputCore
+ + BufferKindUser<BufferKind = Eager>
+ + Default
+ + Clone,
+ <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
+ Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
+{
+ hkdf_impl: hkdf::Hkdf<D>,
+}
+
+impl<D> crypto_provider::hkdf::Hkdf for Hkdf<D>
+where
+ D: OutputSizeUser,
+ D: CoreProxy,
+ D::Core: HashMarker
+ + UpdateCore
+ + FixedOutputCore
+ + BufferKindUser<BufferKind = Eager>
+ + Default
+ + Clone,
+ <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
+ Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
+{
+ fn new(salt: Option<&[u8]>, ikm: &[u8]) -> Self {
+ Hkdf {
+ hkdf_impl: hkdf::Hkdf::new(salt, ikm),
+ }
+ }
+
+ fn expand_multi_info(
+ &self,
+ info_components: &[&[u8]],
+ okm: &mut [u8],
+ ) -> Result<(), InvalidLength> {
+ self.hkdf_impl
+ .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)
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs
new file mode 100644
index 0000000..e31b815
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/hmac_rc.rs
@@ -0,0 +1,114 @@
+// 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::hmac::{InvalidLength, MacError};
+use hmac::digest::block_buffer::Eager;
+use hmac::digest::consts::U256;
+use hmac::digest::core_api::{
+ BlockSizeUser, BufferKindUser, CoreProxy, FixedOutputCore, UpdateCore,
+};
+use hmac::digest::typenum::{IsLess, Le, NonZero};
+use hmac::digest::{HashMarker, OutputSizeUser};
+use hmac::Mac;
+
+/// RustCrypto based hmac implementation
+pub struct Hmac<D>
+where
+ D: OutputSizeUser,
+ D: CoreProxy,
+ D::Core: HashMarker
+ + UpdateCore
+ + FixedOutputCore
+ + BufferKindUser<BufferKind = Eager>
+ + Default
+ + Clone,
+ <D::Core as BlockSizeUser>::BlockSize: IsLess<U256>,
+ Le<<D::Core as BlockSizeUser>::BlockSize, U256>: NonZero,
+{
+ hmac_impl: hmac::Hmac<D>,
+}
+
+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 })
+ .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)
+ }
+
+ fn update(&mut self, data: &[u8]) {
+ self.hmac_impl.update(data);
+ }
+
+ fn finalize(self) -> [u8; 32] {
+ self.hmac_impl.finalize().into_bytes().into()
+ }
+
+ fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
+ self.hmac_impl.verify_slice(tag).map_err(|_| MacError)
+ }
+
+ fn verify(self, tag: [u8; 32]) -> Result<(), MacError> {
+ self.hmac_impl.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)
+ }
+}
+
+impl crypto_provider::hmac::Hmac<64> for Hmac<sha2::Sha512> {
+ #[allow(clippy::expect_used)]
+ fn new_from_key(key: [u8; 64]) -> Self {
+ hmac::Hmac::new_from_slice(&key)
+ .map(|hmac| Self { hmac_impl: hmac })
+ .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)
+ }
+
+ fn update(&mut self, data: &[u8]) {
+ self.hmac_impl.update(data);
+ }
+
+ fn finalize(self) -> [u8; 64] {
+ self.hmac_impl.finalize().into_bytes().into()
+ }
+
+ fn verify_slice(self, tag: &[u8]) -> Result<(), MacError> {
+ self.hmac_impl.verify_slice(tag).map_err(|_| MacError)
+ }
+
+ fn verify(self, tag: [u8; 64]) -> Result<(), MacError> {
+ self.hmac_impl.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)
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
new file mode 100644
index 0000000..9807f81
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/lib.rs
@@ -0,0 +1,127 @@
+// 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.
+#![no_std]
+#![forbid(unsafe_code)]
+#![deny(
+ missing_docs,
+ clippy::indexing_slicing,
+ clippy::unwrap_used,
+ clippy::panic,
+ clippy::expect_used
+)]
+
+//! Crate which provides impls for CryptoProvider backed by RustCrypto crates
+
+/// Contains the RustCrypto backed AES impl for CryptoProvider
+pub mod aes;
+/// 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;
+/// Contains the RustCrypto backed hmac impl for CryptoProvider
+mod hmac_rc;
+/// Contains the RustCrypto backed P256 impl for CryptoProvider
+mod p256;
+/// Contains the RustCrypto backed SHA2 impl for CryptoProvider
+mod sha2_rc;
+/// Contains the RustCrypto backed X25519 impl for CryptoProvider
+mod x25519;
+
+pub use hkdf;
+pub use hmac;
+
+use cfg_if::cfg_if;
+use core::{fmt::Debug, marker::PhantomData};
+use rand::{RngCore, SeedableRng};
+use rand_core::CryptoRng;
+use subtle::ConstantTimeEq;
+
+cfg_if! {
+ if #[cfg(feature = "std")] {
+ /// 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 {
+ /// A no_std compatible implementation of CryptoProvider backed by RustCrypto crates
+ pub type RustCrypto = RustCryptoImpl<rand_chacha::ChaCha20Rng>;
+ }
+}
+
+/// The the RustCrypto backed struct which implements CryptoProvider
+#[derive(Default, Clone, Debug, PartialEq, Eq)]
+pub struct RustCryptoImpl<R: CryptoRng + SeedableRng + RngCore> {
+ _marker: PhantomData<R>,
+}
+
+impl<R: CryptoRng + SeedableRng + RngCore> RustCryptoImpl<R> {
+ ///
+ pub fn new() -> Self {
+ Self {
+ _marker: Default::default(),
+ }
+ }
+}
+
+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 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::AesCtr128;
+ type AesCtr256 = aes::AesCtr256;
+ type Ed25519 = ed25519::Ed25519;
+ type CryptoRng = RcRng<R>;
+
+ fn constant_time_eq(a: &[u8], b: &[u8]) -> bool {
+ a.ct_eq(b).into()
+ }
+}
+
+/// A RustCrypto wrapper for RNG
+pub struct RcRng<R>(R);
+
+impl<R: rand_core::CryptoRng + RngCore + SeedableRng> crypto_provider::CryptoRng for RcRng<R> {
+ fn new() -> Self {
+ Self(R::from_entropy())
+ }
+
+ fn next_u64(&mut self) -> u64 {
+ self.0.next_u64()
+ }
+}
+
+#[cfg(test)]
+mod testing;
+
+#[cfg(test)]
+mod tests {
+ use crate::RustCrypto;
+ use core::marker::PhantomData;
+ use crypto_provider::sha2::testing::*;
+
+ #[apply(sha2_test_cases)]
+ fn sha2_tests(testcase: CryptoProviderTestCase<RustCrypto>) {
+ testcase(PhantomData::<RustCrypto>);
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs b/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs
new file mode 100644
index 0000000..a50bec8
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/p256.rs
@@ -0,0 +1,149 @@
+// 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 crate::RcRng;
+use alloc::vec::Vec;
+use core::marker::PhantomData;
+use crypto_provider::{
+ elliptic_curve::{EcdhProvider, EphemeralSecret},
+ p256::P256,
+};
+use p256::{
+ elliptic_curve,
+ elliptic_curve::{
+ generic_array::GenericArray,
+ sec1::{FromEncodedPoint, ToEncodedPoint},
+ },
+};
+use rand::{RngCore, SeedableRng};
+use rand_core::CryptoRng;
+
+/// Implementation of NIST-P256 using RustCrypto crates.
+pub struct P256Ecdh<R> {
+ _marker: PhantomData<R>,
+}
+
+impl<R: CryptoRng + SeedableRng + RngCore + Send> EcdhProvider<P256> for P256Ecdh<R> {
+ type PublicKey = P256PublicKey;
+ type EphemeralSecret = P256EphemeralSecret<R>;
+ type SharedSecret = [u8; 32];
+}
+
+/// A NIST-P256 public key.
+#[derive(Debug, PartialEq, Eq)]
+pub struct P256PublicKey(p256::PublicKey);
+impl crypto_provider::p256::P256PublicKey for P256PublicKey {
+ type Error = elliptic_curve::Error;
+
+ fn from_sec1_bytes(bytes: &[u8]) -> Result<Self, Self::Error> {
+ 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()
+ }
+
+ #[allow(clippy::expect_used)]
+ fn to_affine_coordinates(&self) -> Result<([u8; 32], [u8; 32]), Self::Error> {
+ let p256_key = self.0.to_encoded_point(false);
+ let x: &[u8; 32] = p256_key
+ .x()
+ .expect("Generated key should not be on identity point")
+ .as_ref();
+ let y: &[u8; 32] = p256_key
+ .y()
+ .expect("Generated key should not be on identity point")
+ .as_ref();
+ Ok((*x, *y))
+ }
+ fn from_affine_coordinates(x: &[u8; 32], y: &[u8; 32]) -> Result<Self, Self::Error> {
+ let key_option: Option<p256::PublicKey> =
+ p256::PublicKey::from_encoded_point(&p256::EncodedPoint::from_affine_coordinates(
+ &GenericArray::clone_from_slice(x),
+ &GenericArray::clone_from_slice(y),
+ false,
+ ))
+ .into();
+ key_option.map(Self).ok_or(elliptic_curve::Error)
+ }
+}
+
+/// Ephemeral secrect for use in a P256 Diffie-Hellman
+pub struct P256EphemeralSecret<R: CryptoRng + SeedableRng + RngCore> {
+ secret: p256::ecdh::EphemeralSecret,
+ _marker: PhantomData<R>,
+}
+
+impl<R: CryptoRng + SeedableRng + RngCore + Send> EphemeralSecret<P256> for P256EphemeralSecret<R> {
+ type Impl = P256Ecdh<R>;
+ type Error = sec1::Error;
+ type Rng = RcRng<R>;
+
+ fn generate_random(rng: &mut Self::Rng) -> Self {
+ Self {
+ secret: p256::ecdh::EphemeralSecret::random(&mut rng.0),
+ _marker: Default::default(),
+ }
+ }
+
+ fn public_key_bytes(&self) -> Vec<u8> {
+ self.secret
+ .public_key()
+ .to_encoded_point(false)
+ .as_bytes()
+ .into()
+ }
+
+ fn diffie_hellman(
+ self,
+ other_pub: &P256PublicKey,
+ ) -> Result<<Self::Impl as EcdhProvider<P256>>::SharedSecret, Self::Error> {
+ let shared_secret = p256::ecdh::EphemeralSecret::diffie_hellman(&self.secret, &other_pub.0);
+ let bytes: <Self::Impl as EcdhProvider<P256>>::SharedSecret =
+ (*shared_secret.raw_secret_bytes()).into();
+ Ok(bytes)
+ }
+}
+
+#[cfg(test)]
+impl<R: CryptoRng + SeedableRng + RngCore + Send>
+ crypto_provider::elliptic_curve::EphemeralSecretForTesting<P256> for P256EphemeralSecret<R>
+{
+ fn from_private_components(
+ private_bytes: &[u8; 32],
+ _public_key: &P256PublicKey,
+ ) -> Result<Self, Self::Error> {
+ Ok(Self {
+ secret: p256::ecdh::EphemeralSecret::random(&mut crate::testing::MockCryptoRng {
+ values: private_bytes.iter(),
+ }),
+ _marker: Default::default(),
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::P256Ecdh;
+ use core::marker::PhantomData;
+ use crypto_provider::p256::testing::*;
+ use rand::rngs::StdRng;
+
+ #[apply(p256_test_cases)]
+ fn p256_tests(testcase: CryptoProviderTestCase<P256Ecdh<StdRng>>) {
+ testcase(PhantomData::<P256Ecdh<StdRng>>)
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs b/nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs
new file mode 100644
index 0000000..977eb83
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/sha2_rc.rs
@@ -0,0 +1,37 @@
+// 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 sha2::Digest;
+
+/// RustCrypto implementation for SHA256
+pub struct RustCryptoSha256;
+
+impl crypto_provider::sha2::Sha256 for RustCryptoSha256 {
+ fn sha256(input: &[u8]) -> [u8; 32] {
+ let mut digest = sha2::Sha256::new();
+ digest.update(input);
+ digest.finalize().into()
+ }
+}
+
+/// RustCrypto implementation for SHA512
+pub struct RustCryptoSha512;
+
+impl crypto_provider::sha2::Sha512 for RustCryptoSha512 {
+ fn sha512(input: &[u8]) -> [u8; 64] {
+ let mut digest = sha2::Sha512::new();
+ digest.update(input);
+ digest.finalize().into()
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/testing.rs b/nearby/crypto/crypto_provider_rustcrypto/src/testing.rs
new file mode 100644
index 0000000..f606492
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/testing.rs
@@ -0,0 +1,51 @@
+#![allow(clippy::expect_used)]
+// 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(test)]
+/// A mock implementation of a CryptoRng that returns the given bytes in `values` in
+/// sequence.
+pub(crate) struct MockCryptoRng<'a, I: Iterator<Item = &'a u8>> {
+ pub(crate) values: I,
+}
+
+impl<'a, I: Iterator<Item = &'a u8>> rand::CryptoRng for MockCryptoRng<'a, I> {}
+
+impl<'a, I: Iterator<Item = &'a u8>> rand::RngCore for MockCryptoRng<'a, I> {
+ fn fill_bytes(&mut self, dest: &mut [u8]) {
+ for i in dest {
+ *i = *self
+ .values
+ .next()
+ .expect("Expecting more data in MockCryptoRng input");
+ }
+ }
+
+ fn next_u32(&mut self) -> u32 {
+ let mut bytes = [0; 4];
+ self.fill_bytes(&mut bytes);
+ u32::from_le_bytes(bytes)
+ }
+
+ fn next_u64(&mut self) -> u64 {
+ let mut bytes = [0; 8];
+ self.fill_bytes(&mut bytes);
+ u64::from_le_bytes(bytes)
+ }
+
+ fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
+ self.fill_bytes(dest);
+ Ok(())
+ }
+}
diff --git a/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs b/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs
new file mode 100644
index 0000000..a184d44
--- /dev/null
+++ b/nearby/crypto/crypto_provider_rustcrypto/src/x25519.rs
@@ -0,0 +1,122 @@
+// 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 crate::RcRng;
+use alloc::vec::Vec;
+use core::marker::PhantomData;
+use crypto_provider::elliptic_curve::{EcdhProvider, EphemeralSecret, PublicKey};
+use crypto_provider::x25519::X25519;
+use rand::RngCore;
+use rand_core::{CryptoRng, SeedableRng};
+
+/// The RustCrypto implementation of X25519 ECDH.
+pub struct X25519Ecdh<R> {
+ _marker: PhantomData<R>,
+}
+
+impl<R: CryptoRng + RngCore + SeedableRng + Send> EcdhProvider<X25519> for X25519Ecdh<R> {
+ type PublicKey = X25519PublicKey;
+ type EphemeralSecret = X25519EphemeralSecret<R>;
+ type SharedSecret = [u8; 32];
+}
+
+/// A X25519 ephemeral secret used for Diffie-Hellman.
+pub struct X25519EphemeralSecret<R: CryptoRng + RngCore + SeedableRng> {
+ secret: x25519_dalek::EphemeralSecret,
+ marker: PhantomData<R>,
+}
+
+impl<R: CryptoRng + RngCore + SeedableRng + Send> EphemeralSecret<X25519>
+ for X25519EphemeralSecret<R>
+{
+ type Impl = X25519Ecdh<R>;
+ type Error = Error;
+ type Rng = RcRng<R>;
+
+ fn generate_random(rng: &mut Self::Rng) -> Self {
+ Self {
+ secret: x25519_dalek::EphemeralSecret::new(&mut rng.0),
+ marker: Default::default(),
+ }
+ }
+
+ fn public_key_bytes(&self) -> Vec<u8> {
+ let pubkey: x25519_dalek::PublicKey = (&self.secret).into();
+ pubkey.to_bytes().into()
+ }
+
+ fn diffie_hellman(
+ self,
+ other_pub: &<X25519Ecdh<R> as EcdhProvider<X25519>>::PublicKey,
+ ) -> Result<<X25519Ecdh<R> as EcdhProvider<X25519>>::SharedSecret, Self::Error> {
+ Ok(x25519_dalek::EphemeralSecret::diffie_hellman(self.secret, &other_pub.0).to_bytes())
+ }
+}
+
+#[cfg(test)]
+impl<R: CryptoRng + RngCore + SeedableRng + Send>
+ crypto_provider::elliptic_curve::EphemeralSecretForTesting<X25519>
+ for X25519EphemeralSecret<R>
+{
+ fn from_private_components(
+ private_bytes: &[u8; 32],
+ _public_key: &X25519PublicKey,
+ ) -> Result<Self, Self::Error> {
+ Ok(Self {
+ secret: x25519_dalek::EphemeralSecret::new(&mut crate::testing::MockCryptoRng {
+ values: private_bytes.iter(),
+ }),
+ marker: Default::default(),
+ })
+ }
+}
+
+/// A X25519 public key.
+#[derive(Debug, PartialEq, Eq)]
+pub struct X25519PublicKey(x25519_dalek::PublicKey);
+
+impl PublicKey<X25519> for X25519PublicKey {
+ type Error = Error;
+
+ 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()
+ }
+}
+
+/// Error type for the RustCrypto 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::x25519::testing::*;
+ use rand::rngs::StdRng;
+
+ #[apply(x25519_test_cases)]
+ fn x25519_tests(testcase: CryptoProviderTestCase<X25519Ecdh<StdRng>>) {
+ testcase(PhantomData::<X25519Ecdh<StdRng>>)
+ }
+}