| // 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)] |
| |
| //! Crypto abstraction trait only crate, which provides traits for cryptographic primitives |
| |
| use core::fmt::Debug; |
| |
| /// mod containing hmac trait |
| pub mod hkdf; |
| |
| /// mod containing hkdf trait |
| pub mod hmac; |
| |
| /// mod containing X25519 trait |
| pub mod x25519; |
| |
| /// mod containing traits for NIST-P256 elliptic curve implementation. |
| pub mod p256; |
| |
| /// mod containing traits for elliptic curve cryptography. |
| pub mod elliptic_curve; |
| |
| /// mod containing SHA256 trait. |
| pub mod sha2; |
| |
| /// mod containing aes trait |
| pub mod aes; |
| |
| /// mod containing traits for ed25519 key generation, signing, and verification |
| pub mod ed25519; |
| |
| /// 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 |
| type HkdfSha256: hkdf::Hkdf; |
| /// The Hmac type which implements the hmac trait |
| type HmacSha256: hmac::Hmac<32>; |
| /// The Hkdf type which implements the hkdf trait |
| type HkdfSha512: hkdf::Hkdf; |
| /// The Hmac type which implements the hmac trait |
| type HmacSha512: hmac::Hmac<64>; |
| /// The AES-CBC-PKCS7 implementation to use |
| type AesCbcPkcs7Padded: aes::cbc::AesCbcPkcs7Padded; |
| /// The X25519 implementation to use for ECDH. |
| type X25519: elliptic_curve::EcdhProvider<x25519::X25519>; |
| /// The P256 implementation to use for ECDH. |
| type P256: p256::P256EcdhProvider; |
| /// The SHA256 hash implementation. |
| type Sha256: sha2::Sha256; |
| /// The SHA512 hash implementation. |
| type Sha512: sha2::Sha512; |
| /// Plain AES-128 implementation (without block cipher mode). |
| type Aes128: aes::Aes<Key = Aes128Key>; |
| /// Plain AES-256 implementation (without block cipher mode). |
| type Aes256: aes::Aes<Key = Aes256Key>; |
| /// AES-128 with CTR block mode |
| type AesCtr128: aes::ctr::AesCtr<Key = aes::Aes128Key>; |
| /// AES-256 with CTR block mode |
| type AesCtr256: aes::ctr::AesCtr<Key = aes::Aes256Key>; |
| /// The trait defining ed25519, a Edwards-curve Digital Signature Algorithm signature scheme |
| /// using SHA-512 (SHA-2) and Curve25519 |
| type Ed25519: ed25519::Ed25519Provider; |
| |
| /// The cryptographically secure random number generator |
| type CryptoRng: CryptoRng; |
| |
| /// Compares the two given slices, in constant time, and returns true if they are equal. |
| fn constant_time_eq(a: &[u8], b: &[u8]) -> bool; |
| } |
| |
| /// Wrapper to a cryptographically secure pseudo random number generator |
| pub trait CryptoRng { |
| /// Returns an instance of the rng |
| fn new() -> Self; |
| |
| ///Return the next random u64 |
| fn next_u64(&mut self) -> u64; |
| } |
| |
| /// If impls want to opt out of passing a Rng they can simply use `()` for the Rng associated type |
| impl CryptoRng for () { |
| fn new() -> Self {} |
| |
| fn next_u64(&mut self) -> u64 { |
| unimplemented!() |
| } |
| } |
| |
| use crate::aes::{Aes128Key, Aes256Key}; |
| #[cfg(feature = "testing")] |
| pub use rstest_reuse; |
| |
| /// Utilities for testing implementations of this crate. |
| #[cfg(feature = "testing")] |
| pub mod testing { |
| extern crate alloc; |
| use crate::CryptoProvider; |
| use alloc::{format, string::String}; |
| use core::marker::PhantomData; |
| use hex_literal::hex; |
| use rand::{Rng, RngCore}; |
| use rstest_reuse::template; |
| |
| /// Common items that needs to be imported to use these test cases |
| pub mod prelude { |
| pub use super::CryptoProviderTestCase; |
| pub use rstest::rstest; |
| pub use rstest_reuse; |
| pub use rstest_reuse::apply; |
| } |
| |
| /// A test case for Crypto Provider. A test case is a function that panics if the test fails. |
| pub type CryptoProviderTestCase<T> = fn(PhantomData<T>); |
| |
| #[derive(Debug)] |
| pub(crate) struct TestError(String); |
| |
| impl TestError { |
| pub(crate) fn new<D: core::fmt::Debug>(value: D) -> Self { |
| Self(format!("{value:?}")) |
| } |
| } |
| |
| /// Test for `constant_time_eq` when the two inputs are equal. |
| pub fn constant_time_eq_test_equal<C: CryptoProvider>(_marker: PhantomData<C>) { |
| assert!(C::constant_time_eq( |
| &hex!("00010203040506070809"), |
| &hex!("00010203040506070809") |
| )); |
| } |
| |
| /// Test for `constant_time_eq` when the two inputs are not equal. |
| pub fn constant_time_eq_test_not_equal<C: CryptoProvider>(_marker: PhantomData<C>) { |
| assert!(!C::constant_time_eq( |
| &hex!("00010203040506070809"), |
| &hex!("00000000000000000000") |
| )); |
| } |
| |
| /// Random tests for `constant_time_eq`. |
| pub fn constant_time_eq_random_test<C: CryptoProvider>(_marker: PhantomData<C>) { |
| let mut rng = rand::thread_rng(); |
| for _ in 1..100 { |
| // Test using "oracle" of ==, with possibly different lengths for a and b |
| let mut a = alloc::vec![0; rng.gen_range(1..1000)]; |
| rng.fill_bytes(&mut a); |
| let mut b = alloc::vec![0; rng.gen_range(1..1000)]; |
| rng.fill_bytes(&mut b); |
| assert_eq!(C::constant_time_eq(&a, &b), a == b); |
| } |
| |
| for _ in 1..10000 { |
| // Test using "oracle" of ==, with same lengths for a and b |
| let len = rng.gen_range(1..1000); |
| let mut a = alloc::vec![0; len]; |
| rng.fill_bytes(&mut a); |
| let mut b = alloc::vec![0; len]; |
| rng.fill_bytes(&mut b); |
| assert_eq!(C::constant_time_eq(&a, &b), a == b); |
| } |
| |
| for _ in 1..10000 { |
| // Clones and the original should always be equal |
| let mut a = alloc::vec![0; rng.gen_range(1..1000)]; |
| rng.fill_bytes(&mut a); |
| assert!(C::constant_time_eq(&a, &a.clone())); |
| } |
| } |
| |
| /// Generates the test cases to validate the P256 implementation. |
| /// For example, to test `MyCryptoProvider`: |
| /// |
| /// ``` |
| /// use crypto_provider::p256::testing::*; |
| /// |
| /// mod tests { |
| /// #[apply(constant_time_eq_test_cases)] |
| /// fn constant_time_eq_tests( |
| /// testcase: CryptoProviderTestCase<MyCryptoProvider>) { |
| /// testcase(PhantomData); |
| /// } |
| /// } |
| /// ``` |
| #[template] |
| #[export] |
| #[rstest] |
| #[case::constant_time_eq_test_not_equal(constant_time_eq_test_not_equal)] |
| #[case::constant_time_eq_test_equal(constant_time_eq_test_equal)] |
| #[case::constant_time_eq_random_test(constant_time_eq_random_test)] |
| fn constant_time_eq_test_cases<C: CryptoProvider>(#[case] testcase: CryptoProviderTestCase<C>) { |
| } |
| } |