diff --git a/libraries/opensk/Cargo.toml b/libraries/opensk/Cargo.toml index 228041e..c469a74 100644 --- a/libraries/opensk/Cargo.toml +++ b/libraries/opensk/Cargo.toml @@ -27,6 +27,8 @@ rand_core = { version = "0.6.4", optional = true } sha2 = { version = "0.10.6", optional = true } hmac = { version = "0.12.1", optional = true } hkdf = { version = "0.12.3", optional = true } +aes = { version = "0.8.2", optional = true } +cbc = { version = "0.1.2", optional = true } [features] debug_ctap = [] @@ -35,7 +37,7 @@ with_ctap1 = ["crypto/with_ctap1"] vendor_hid = [] fuzz = ["arbitrary", "std"] ed25519 = ["ed25519-compact"] -rust_crypto = ["p256", "rand_core", "sha2", "hmac", "hkdf"] +rust_crypto = ["p256", "rand_core", "sha2", "hmac", "hkdf", "aes", "cbc"] [dev-dependencies] enum-iterator = "0.6.0" diff --git a/libraries/opensk/src/api/crypto/aes256.rs b/libraries/opensk/src/api/crypto/aes256.rs new file mode 100644 index 0000000..dbd28a0 --- /dev/null +++ b/libraries/opensk/src/api/crypto/aes256.rs @@ -0,0 +1,41 @@ +// 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 super::{AES_BLOCK_SIZE, AES_KEY_SIZE}; + +/// Encrypts and decrypts data using AES256. +pub trait Aes256 { + /// Creates a new key from its bytes. + fn new(key: &[u8; AES_KEY_SIZE]) -> Self; + + /// Encrypts a block in place. + fn encrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]); + + /// Decrypts a block in place. + fn decrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]); + + /// Encrypts a message in place using CBC mode. + /// + /// # Panics + /// + /// Panics if the plaintext is not a multiple of the block size. + fn encrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], plaintext: &mut [u8]); + + /// Decrypts a message in place using CBC mode. + /// + /// # Panics + /// + /// Panics if the ciphertext is not a multiple of the block size. + fn decrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], ciphertext: &mut [u8]); +} diff --git a/libraries/opensk/src/api/crypto/mod.rs b/libraries/opensk/src/api/crypto/mod.rs index 0936e55..006ad4b 100644 --- a/libraries/opensk/src/api/crypto/mod.rs +++ b/libraries/opensk/src/api/crypto/mod.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +pub mod aes256; pub mod ecdh; pub mod ecdsa; #[cfg(feature = "rust_crypto")] @@ -24,12 +25,19 @@ pub mod hkdf256; pub mod hmac256; pub mod sha256; +use self::aes256::Aes256; use self::ecdh::Ecdh; use self::ecdsa::Ecdsa; use self::hkdf256::Hkdf256; use self::hmac256::Hmac256; use self::sha256::Sha256; +/// The size of a serialized ECDSA signature. +pub const AES_BLOCK_SIZE: usize = 16; + +/// The size of field elements in the elliptic curve P256. +pub const AES_KEY_SIZE: usize = 32; + /// The size of field elements in the elliptic curve P256. pub const EC_FIELD_SIZE: usize = 32; @@ -49,9 +57,140 @@ pub const TRUNCATED_HMAC_SIZE: usize = 16; /// Necessary cryptographic primitives for CTAP. pub trait Crypto { + type Aes256: Aes256; type Ecdh: Ecdh; type Ecdsa: Ecdsa; type Sha256: Sha256; type Hmac256: Hmac256; type Hkdf256: Hkdf256; } + +#[cfg(test)] +mod test { + use super::software_crypto::*; + use super::*; + use crate::api::crypto::ecdh::{PublicKey as _, SecretKey as _, SharedSecret}; + use crate::api::crypto::ecdsa::{PublicKey as _, SecretKey as _}; + use crate::env::test::TestEnv; + use core::convert::TryFrom; + + #[test] + fn test_shared_secret_symmetric() { + let mut env = TestEnv::default(); + let private1 = SoftwareEcdhSecretKey::random(env.rng()); + let private2 = SoftwareEcdhSecretKey::random(env.rng()); + let pub1 = private1.public_key(); + let pub2 = private2.public_key(); + let shared1 = private1.diffie_hellman(&pub2); + let shared2 = private2.diffie_hellman(&pub1); + assert_eq!(shared1.raw_secret_bytes(), shared2.raw_secret_bytes()); + } + + #[test] + fn test_ecdh_public_key_from_to_bytes() { + let mut env = TestEnv::default(); + let first_key = SoftwareEcdhSecretKey::random(env.rng()); + let first_public = first_key.public_key(); + let mut x = [0; EC_FIELD_SIZE]; + let mut y = [0; EC_FIELD_SIZE]; + first_public.to_coordinates(&mut x, &mut y); + let new_public = SoftwareEcdhPublicKey::from_coordinates(&x, &y).unwrap(); + let mut new_x = [0; EC_FIELD_SIZE]; + let mut new_y = [0; EC_FIELD_SIZE]; + new_public.to_coordinates(&mut new_x, &mut new_y); + assert_eq!(x, new_x); + assert_eq!(y, new_y); + } + + #[test] + fn test_sign_verify() { + let mut env = TestEnv::default(); + let private_key = SoftwareEcdsaSecretKey::random(env.rng()); + let public_key = private_key.public_key(); + let message = [0x12, 0x34, 0x56, 0x78]; + let signature = private_key.sign(&message); + assert!(public_key.verify(&message, &signature)); + } + + #[test] + fn test_ecdsa_secret_key_from_to_bytes() { + let mut env = TestEnv::default(); + let first_key = SoftwareEcdsaSecretKey::random(env.rng()); + let mut key_bytes = [0; EC_FIELD_SIZE]; + first_key.to_slice(&mut key_bytes); + let second_key = SoftwareEcdsaSecretKey::from_slice(&key_bytes).unwrap(); + let mut new_bytes = [0; EC_FIELD_SIZE]; + second_key.to_slice(&mut new_bytes); + assert_eq!(key_bytes, new_bytes); + } + + #[test] + fn test_sha256_hash_matches() { + let data = [0x55; 16]; + let mut hasher = SoftwareSha256::new(); + hasher.update(&data); + assert_eq!(SoftwareSha256::digest(&data), hasher.finalize()); + } + + #[test] + fn test_hmac256_verifies() { + let key = [0xAA; HMAC_KEY_SIZE]; + let data = [0x55; 16]; + let mac = SoftwareHmac256::mac(&key, &data); + assert!(SoftwareHmac256::verify(&key, &data, &mac)); + let truncated_mac = + <&[u8; TRUNCATED_HMAC_SIZE]>::try_from(&mac[..TRUNCATED_HMAC_SIZE]).unwrap(); + assert!(SoftwareHmac256::verify_truncated_left( + &key, + &data, + &truncated_mac + )); + } + + #[test] + fn test_hkdf_empty_salt_256_vector() { + let okm = [ + 0xf9, 0xbe, 0x72, 0x11, 0x6c, 0xb9, 0x7f, 0x41, 0x82, 0x82, 0x10, 0x28, 0x9c, 0xaa, + 0xfe, 0xab, 0xde, 0x1f, 0x3d, 0xfb, 0x97, 0x23, 0xbf, 0x43, 0x53, 0x8a, 0xb1, 0x8f, + 0x36, 0x66, 0x78, 0x3a, + ]; + assert_eq!(&SoftwareHkdf256::hkdf_empty_salt_256(b"0", &[0]), &okm); + } + + #[test] + fn test_aes_encrypt_decrypt_block() { + let mut block = [0x55; AES_BLOCK_SIZE]; + let aes = SoftwareAes256::new(&[0xAA; AES_KEY_SIZE]); + aes.encrypt_block(&mut block); + aes.decrypt_block(&mut block); + assert_eq!(block, [0x55; AES_BLOCK_SIZE]); + } + + #[test] + fn test_aes_encrypt_decrypt_cbc() { + let mut message = [0x55; 2 * AES_BLOCK_SIZE]; + let iv = [0x11; AES_BLOCK_SIZE]; + let aes = SoftwareAes256::new(&[0xAA; AES_KEY_SIZE]); + aes.encrypt_cbc(&iv, &mut message); + aes.decrypt_cbc(&iv, &mut message); + assert_eq!(message, [0x55; 2 * AES_BLOCK_SIZE]); + } + + #[test] + #[should_panic] + fn test_aes_encrypt_panics() { + let mut message = [0x55; AES_BLOCK_SIZE + 1]; + let iv = [0x11; AES_BLOCK_SIZE]; + let aes = SoftwareAes256::new(&[0xAA; AES_KEY_SIZE]); + aes.encrypt_cbc(&iv, &mut message); + } + + #[test] + #[should_panic] + fn test_aes_decrypt_panics() { + let mut message = [0x55; AES_BLOCK_SIZE + 1]; + let iv = [0x11; AES_BLOCK_SIZE]; + let aes = SoftwareAes256::new(&[0xAA; AES_KEY_SIZE]); + aes.decrypt_cbc(&iv, &mut message); + } +} diff --git a/libraries/opensk/src/api/crypto/rust_crypto.rs b/libraries/opensk/src/api/crypto/rust_crypto.rs index 752657b..efdf284 100644 --- a/libraries/opensk/src/api/crypto/rust_crypto.rs +++ b/libraries/opensk/src/api/crypto/rust_crypto.rs @@ -20,12 +20,17 @@ //! //! If you want to use OpenSK outside of Tock v1, maybe this is useful for you though! +use crate::api::crypto::aes256::Aes256; use crate::api::crypto::hkdf256::Hkdf256; use crate::api::crypto::hmac256::Hmac256; use crate::api::crypto::sha256::Sha256; use crate::api::crypto::{ - ecdh, ecdsa, Crypto, EC_FIELD_SIZE, EC_SIGNATURE_SIZE, HASH_SIZE, HMAC_KEY_SIZE, - TRUNCATED_HMAC_SIZE, + ecdh, ecdsa, Crypto, AES_BLOCK_SIZE, AES_KEY_SIZE, EC_FIELD_SIZE, EC_SIGNATURE_SIZE, HASH_SIZE, + HMAC_KEY_SIZE, TRUNCATED_HMAC_SIZE, +}; +use aes::cipher::generic_array::GenericArray; +use aes::cipher::{ + BlockDecrypt, BlockDecryptMut, BlockEncrypt, BlockEncryptMut, KeyInit, KeyIvInit, }; use core::convert::TryFrom; use hmac::Mac; @@ -43,6 +48,7 @@ pub struct SoftwareEcdh; pub struct SoftwareEcdsa; impl Crypto for SoftwareCrypto { + type Aes256 = SoftwareAes256; type Ecdh = SoftwareEcdh; type Ecdsa = SoftwareEcdsa; type Sha256 = SoftwareSha256; @@ -228,13 +234,13 @@ pub struct SoftwareHmac256; impl Hmac256 for SoftwareHmac256 { fn mac(key: &[u8; HMAC_KEY_SIZE], data: &[u8]) -> [u8; HASH_SIZE] { - let mut hmac = hmac::Hmac::::new_from_slice(key).unwrap(); + let mut hmac = as hmac::Mac>::new_from_slice(key).unwrap(); hmac.update(data); hmac.finalize().into_bytes().into() } fn verify(key: &[u8; HMAC_KEY_SIZE], data: &[u8], mac: &[u8; HASH_SIZE]) -> bool { - let mut hmac = hmac::Hmac::::new_from_slice(key).unwrap(); + let mut hmac = as hmac::Mac>::new_from_slice(key).unwrap(); hmac.update(data); hmac.verify_slice(mac).is_ok() } @@ -244,7 +250,7 @@ impl Hmac256 for SoftwareHmac256 { data: &[u8], mac: &[u8; TRUNCATED_HMAC_SIZE], ) -> bool { - let mut hmac = hmac::Hmac::::new_from_slice(key).unwrap(); + let mut hmac = as hmac::Mac>::new_from_slice(key).unwrap(); hmac.update(data); hmac.verify_truncated_left(mac).is_ok() } @@ -261,95 +267,36 @@ impl Hkdf256 for SoftwareHkdf256 { } } -#[cfg(test)] -mod test { - use super::*; - use crate::api::crypto::ecdh::{ - PublicKey as EcdhPublicKey, SecretKey as EcdhSecretKey, SharedSecret, - }; - use crate::api::crypto::ecdsa::{PublicKey as EcdsaPublicKey, SecretKey as EcdsaSecretKey}; - use crate::env::test::TestEnv; +pub struct SoftwareAes256 { + key: [u8; AES_KEY_SIZE], +} - #[test] - fn test_shared_secret_symmetric() { - let mut env = TestEnv::default(); - let private1 = SoftwareEcdhSecretKey::random(env.rng()); - let private2 = SoftwareEcdhSecretKey::random(env.rng()); - let pub1 = private1.public_key(); - let pub2 = private2.public_key(); - let shared1 = private1.diffie_hellman(&pub2); - let shared2 = private2.diffie_hellman(&pub1); - assert_eq!(shared1.raw_secret_bytes(), shared2.raw_secret_bytes()); +impl Aes256 for SoftwareAes256 { + fn new(key: &[u8; AES_KEY_SIZE]) -> Self { + SoftwareAes256 { key: key.clone() } } - #[test] - fn test_ecdh_public_key_from_to_bytes() { - let mut env = TestEnv::default(); - let first_key = SoftwareEcdhSecretKey::random(env.rng()); - let first_public = first_key.public_key(); - let mut x = [0; EC_FIELD_SIZE]; - let mut y = [0; EC_FIELD_SIZE]; - first_public.to_coordinates(&mut x, &mut y); - let new_public = SoftwareEcdhPublicKey::from_coordinates(&x, &y).unwrap(); - let mut new_x = [0; EC_FIELD_SIZE]; - let mut new_y = [0; EC_FIELD_SIZE]; - new_public.to_coordinates(&mut new_x, &mut new_y); - assert_eq!(x, new_x); - assert_eq!(y, new_y); + fn encrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]) { + let cipher = aes::Aes256::new_from_slice(&self.key).unwrap(); + cipher.encrypt_block(block.into()); } - #[test] - fn test_sign_verify() { - let mut env = TestEnv::default(); - let private_key = SoftwareEcdsaSecretKey::random(env.rng()); - let public_key = private_key.public_key(); - let message = [0x12, 0x34, 0x56, 0x78]; - let signature = private_key.sign(&message); - assert!(public_key.verify(&message, &signature)); + fn decrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]) { + let cipher = aes::Aes256::new_from_slice(&self.key).unwrap(); + cipher.decrypt_block(block.into()); } - #[test] - fn test_ecdsa_secret_key_from_to_bytes() { - let mut env = TestEnv::default(); - let first_key = SoftwareEcdsaSecretKey::random(env.rng()); - let mut key_bytes = [0; EC_FIELD_SIZE]; - first_key.to_slice(&mut key_bytes); - let second_key = SoftwareEcdsaSecretKey::from_slice(&key_bytes).unwrap(); - let mut new_bytes = [0; EC_FIELD_SIZE]; - second_key.to_slice(&mut new_bytes); - assert_eq!(key_bytes, new_bytes); + fn encrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], plaintext: &mut [u8]) { + let mut encryptor = cbc::Encryptor::::new_from_slices(&self.key, iv).unwrap(); + for block in plaintext.chunks_mut(AES_BLOCK_SIZE) { + encryptor.encrypt_block_mut(GenericArray::from_mut_slice(block)); + } } - #[test] - fn test_sha256_hash_matches() { - let data = [0x55; 16]; - let mut hasher = SoftwareSha256::new(); - hasher.update(&data); - assert_eq!(SoftwareSha256::digest(&data), hasher.finalize()); - } - - #[test] - fn test_hmac256_verifies() { - let key = [0xAA; HMAC_KEY_SIZE]; - let data = [0x55; 16]; - let mac = SoftwareHmac256::mac(&key, &data); - assert!(SoftwareHmac256::verify(&key, &data, &mac)); - let truncated_mac = - <&[u8; TRUNCATED_HMAC_SIZE]>::try_from(&mac[..TRUNCATED_HMAC_SIZE]).unwrap(); - assert!(SoftwareHmac256::verify_truncated_left( - &key, - &data, - &truncated_mac - )); - } - - #[test] - fn test_hkdf_empty_salt_256_vector() { - let okm = [ - 0xf9, 0xbe, 0x72, 0x11, 0x6c, 0xb9, 0x7f, 0x41, 0x82, 0x82, 0x10, 0x28, 0x9c, 0xaa, - 0xfe, 0xab, 0xde, 0x1f, 0x3d, 0xfb, 0x97, 0x23, 0xbf, 0x43, 0x53, 0x8a, 0xb1, 0x8f, - 0x36, 0x66, 0x78, 0x3a, - ]; - assert_eq!(&SoftwareHkdf256::hkdf_empty_salt_256(b"0", &[0]), &okm); + fn decrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], ciphertext: &mut [u8]) { + let mut decryptor = cbc::Decryptor::::new_from_slices(&self.key, iv).unwrap(); + for block in ciphertext.chunks_mut(AES_BLOCK_SIZE) { + decryptor.decrypt_block_mut(GenericArray::from_mut_slice(block)); + } } } diff --git a/libraries/opensk/src/api/crypto/software_crypto.rs b/libraries/opensk/src/api/crypto/software_crypto.rs index 8efa194..7052cb9 100644 --- a/libraries/opensk/src/api/crypto/software_crypto.rs +++ b/libraries/opensk/src/api/crypto/software_crypto.rs @@ -12,12 +12,13 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::api::crypto::aes256::Aes256; use crate::api::crypto::hkdf256::Hkdf256; use crate::api::crypto::hmac256::Hmac256; use crate::api::crypto::sha256::Sha256; use crate::api::crypto::{ - ecdh, ecdsa, Crypto, EC_FIELD_SIZE, EC_SIGNATURE_SIZE, HASH_SIZE, HMAC_KEY_SIZE, - TRUNCATED_HMAC_SIZE, + ecdh, ecdsa, Crypto, AES_BLOCK_SIZE, AES_KEY_SIZE, EC_FIELD_SIZE, EC_SIGNATURE_SIZE, HASH_SIZE, + HMAC_KEY_SIZE, TRUNCATED_HMAC_SIZE, }; use alloc::vec::Vec; use crypto::Hash256; @@ -28,6 +29,7 @@ pub struct SoftwareEcdh; pub struct SoftwareEcdsa; impl Crypto for SoftwareCrypto { + type Aes256 = SoftwareAes256; type Ecdh = SoftwareEcdh; type Ecdsa = SoftwareEcdsa; type Sha256 = SoftwareSha256; @@ -211,96 +213,31 @@ impl Hkdf256 for SoftwareHkdf256 { } } -#[cfg(test)] -mod test { - use super::*; - use crate::api::crypto::ecdh::{ - PublicKey as EcdhPublicKey, SecretKey as EcdhSecretKey, SharedSecret, - }; - use crate::api::crypto::ecdsa::{PublicKey as EcdsaPublicKey, SecretKey as EcdsaSecretKey}; - use crate::env::test::TestEnv; - use core::convert::TryFrom; +pub struct SoftwareAes256 { + enc_key: crypto::aes256::EncryptionKey, +} - #[test] - fn test_shared_secret_symmetric() { - let mut env = TestEnv::default(); - let private1 = SoftwareEcdhSecretKey::random(env.rng()); - let private2 = SoftwareEcdhSecretKey::random(env.rng()); - let pub1 = private1.public_key(); - let pub2 = private2.public_key(); - let shared1 = private1.diffie_hellman(&pub2); - let shared2 = private2.diffie_hellman(&pub1); - assert_eq!(shared1.raw_secret_bytes(), shared2.raw_secret_bytes()); +impl Aes256 for SoftwareAes256 { + fn new(key: &[u8; AES_KEY_SIZE]) -> Self { + let enc_key = crypto::aes256::EncryptionKey::new(key); + SoftwareAes256 { enc_key } } - #[test] - fn test_ecdh_public_key_from_to_bytes() { - let mut env = TestEnv::default(); - let first_key = SoftwareEcdhSecretKey::random(env.rng()); - let first_public = first_key.public_key(); - let mut x = [0; EC_FIELD_SIZE]; - let mut y = [0; EC_FIELD_SIZE]; - first_public.to_coordinates(&mut x, &mut y); - let new_public = SoftwareEcdhPublicKey::from_coordinates(&x, &y).unwrap(); - let mut new_x = [0; EC_FIELD_SIZE]; - let mut new_y = [0; EC_FIELD_SIZE]; - new_public.to_coordinates(&mut new_x, &mut new_y); - assert_eq!(x, new_x); - assert_eq!(y, new_y); + fn encrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]) { + self.enc_key.encrypt_block(block); } - #[test] - fn test_sign_verify() { - let mut env = TestEnv::default(); - let private_key = SoftwareEcdsaSecretKey::random(env.rng()); - let public_key = private_key.public_key(); - let message = [0x12, 0x34, 0x56, 0x78]; - let signature = private_key.sign(&message); - assert!(public_key.verify(&message, &signature)); + fn decrypt_block(&self, block: &mut [u8; AES_BLOCK_SIZE]) { + let dec_key = crypto::aes256::DecryptionKey::new(&self.enc_key); + dec_key.decrypt_block(block); } - #[test] - fn test_ecdsa_secret_key_from_to_bytes() { - let mut env = TestEnv::default(); - let first_key = SoftwareEcdsaSecretKey::random(env.rng()); - let mut key_bytes = [0; EC_FIELD_SIZE]; - first_key.to_slice(&mut key_bytes); - let second_key = SoftwareEcdsaSecretKey::from_slice(&key_bytes).unwrap(); - let mut new_bytes = [0; EC_FIELD_SIZE]; - second_key.to_slice(&mut new_bytes); - assert_eq!(key_bytes, new_bytes); + fn encrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], plaintext: &mut [u8]) { + crypto::cbc::cbc_encrypt(&self.enc_key, *iv, plaintext); } - #[test] - fn test_sha256_hash_matches() { - let data = [0x55; 16]; - let mut hasher = SoftwareSha256::new(); - hasher.update(&data); - assert_eq!(SoftwareSha256::digest(&data), hasher.finalize()); - } - - #[test] - fn test_hmac256_verifies() { - let key = [0xAA; HMAC_KEY_SIZE]; - let data = [0x55; 16]; - let mac = SoftwareHmac256::mac(&key, &data); - assert!(SoftwareHmac256::verify(&key, &data, &mac)); - let truncated_mac = - <&[u8; TRUNCATED_HMAC_SIZE]>::try_from(&mac[..TRUNCATED_HMAC_SIZE]).unwrap(); - assert!(SoftwareHmac256::verify_truncated_left( - &key, - &data, - truncated_mac - )); - } - - #[test] - fn test_hkdf_empty_salt_256_vector() { - let okm = [ - 0xf9, 0xbe, 0x72, 0x11, 0x6c, 0xb9, 0x7f, 0x41, 0x82, 0x82, 0x10, 0x28, 0x9c, 0xaa, - 0xfe, 0xab, 0xde, 0x1f, 0x3d, 0xfb, 0x97, 0x23, 0xbf, 0x43, 0x53, 0x8a, 0xb1, 0x8f, - 0x36, 0x66, 0x78, 0x3a, - ]; - assert_eq!(&SoftwareHkdf256::hkdf_empty_salt_256(b"0", &[0]), &okm); + fn decrypt_cbc(&self, iv: &[u8; AES_BLOCK_SIZE], ciphertext: &mut [u8]) { + let dec_key = crypto::aes256::DecryptionKey::new(&self.enc_key); + crypto::cbc::cbc_decrypt(&dec_key, *iv, ciphertext); } } diff --git a/libraries/opensk/src/ctap/credential_id.rs b/libraries/opensk/src/ctap/credential_id.rs index 6062ef4..fd225ac 100644 --- a/libraries/opensk/src/ctap/credential_id.rs +++ b/libraries/opensk/src/ctap/credential_id.rs @@ -18,10 +18,11 @@ use super::data_formats::{ }; use super::status_code::Ctap2StatusCode; use super::{cbor_read, cbor_write}; +use crate::api::crypto::aes256::Aes256; use crate::api::crypto::hmac256::Hmac256; use crate::api::key_store::KeyStore; use crate::ctap::data_formats::{extract_byte_string, extract_map}; -use crate::env::{Env, Hmac}; +use crate::env::{AesKey, Env, Hmac}; use alloc::string::String; use alloc::vec::Vec; use core::convert::{TryFrom, TryInto}; @@ -64,12 +65,12 @@ impl From for sk_cbor::Value { } } -fn decrypt_legacy_credential_id( - env: &mut impl Env, +fn decrypt_legacy_credential_id( + env: &mut E, bytes: &[u8], ) -> Result, Ctap2StatusCode> { - let aes_enc_key = crypto::aes256::EncryptionKey::new(&env.key_store().key_handle_encryption()?); - let plaintext = aes256_cbc_decrypt(&aes_enc_key, bytes, true)?; + let aes_key = AesKey::::new(&env.key_store().key_handle_encryption()?); + let plaintext = aes256_cbc_decrypt::(&aes_key, bytes, true)?; if plaintext.len() != 64 { return Ok(None); } @@ -86,12 +87,12 @@ fn decrypt_legacy_credential_id( })) } -fn decrypt_cbor_credential_id( - env: &mut impl Env, +fn decrypt_cbor_credential_id( + env: &mut E, bytes: &[u8], ) -> Result, Ctap2StatusCode> { - let aes_enc_key = crypto::aes256::EncryptionKey::new(&env.key_store().key_handle_encryption()?); - let mut plaintext = aes256_cbc_decrypt(&aes_enc_key, bytes, true)?; + let aes_key = AesKey::::new(&env.key_store().key_handle_encryption()?); + let mut plaintext = aes256_cbc_decrypt::(&aes_key, bytes, true)?; remove_padding(&mut plaintext)?; let cbor_credential_source = cbor_read(plaintext.as_slice())?; @@ -177,8 +178,8 @@ pub fn encrypt_to_credential_id( cbor_write(cbor, &mut payload)?; add_padding(&mut payload)?; - let aes_enc_key = crypto::aes256::EncryptionKey::new(&env.key_store().key_handle_encryption()?); - let encrypted_payload = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &payload, true)?; + let aes_key = AesKey::::new(&env.key_store().key_handle_encryption()?); + let encrypted_payload = aes256_cbc_encrypt::(env.rng(), &aes_key, &payload, true)?; let mut credential_id = encrypted_payload; credential_id.insert(0, CBOR_CREDENTIAL_ID_VERSION); @@ -383,13 +384,13 @@ mod test { private_key: EcdsaSk, application: &[u8; 32], ) -> Result, Ctap2StatusCode> { - let aes_enc_key = - crypto::aes256::EncryptionKey::new(&env.key_store().key_handle_encryption()?); + let aes_key = AesKey::::new(&env.key_store().key_handle_encryption()?); let mut plaintext = [0; 64]; private_key.to_slice(array_mut_ref!(plaintext, 0, 32)); plaintext[32..64].copy_from_slice(application); - let mut encrypted_id = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true)?; + let mut encrypted_id = + aes256_cbc_encrypt::(env.rng(), &aes_key, &plaintext, true)?; let id_hmac = Hmac::::mac( &env.key_store().key_handle_authentication()?, &encrypted_id[..], diff --git a/libraries/opensk/src/ctap/crypto_wrapper.rs b/libraries/opensk/src/ctap/crypto_wrapper.rs index 4c9bc80..b308caf 100644 --- a/libraries/opensk/src/ctap/crypto_wrapper.rs +++ b/libraries/opensk/src/ctap/crypto_wrapper.rs @@ -12,23 +12,23 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::api::crypto::aes256::Aes256; use crate::api::crypto::ecdsa::{SecretKey as _, Signature}; use crate::api::key_store::KeyStore; use crate::ctap::data_formats::{extract_array, extract_byte_string, CoseKey, SignatureAlgorithm}; use crate::ctap::status_code::Ctap2StatusCode; -use crate::env::{EcdsaSk, Env}; +use crate::env::{AesKey, EcdsaSk, Env}; use alloc::vec; use alloc::vec::Vec; use core::convert::TryFrom; -use crypto::cbc::{cbc_decrypt, cbc_encrypt}; use rng256::Rng256; use sk_cbor as cbor; use sk_cbor::{cbor_array, cbor_bytes, cbor_int}; /// Wraps the AES256-CBC encryption to match what we need in CTAP. -pub fn aes256_cbc_encrypt( +pub fn aes256_cbc_encrypt( rng: &mut dyn Rng256, - aes_enc_key: &crypto::aes256::EncryptionKey, + aes_key: &AesKey, plaintext: &[u8], embeds_iv: bool, ) -> Result, Ctap2StatusCode> { @@ -46,13 +46,13 @@ pub fn aes256_cbc_encrypt( }; let start = ciphertext.len(); ciphertext.extend_from_slice(plaintext); - cbc_encrypt(aes_enc_key, iv, &mut ciphertext[start..]); + aes_key.encrypt_cbc(&iv, &mut ciphertext[start..]); Ok(ciphertext) } /// Wraps the AES256-CBC decryption to match what we need in CTAP. -pub fn aes256_cbc_decrypt( - aes_enc_key: &crypto::aes256::EncryptionKey, +pub fn aes256_cbc_decrypt( + aes_key: &AesKey, ciphertext: &[u8], embeds_iv: bool, ) -> Result, Ctap2StatusCode> { @@ -61,13 +61,12 @@ pub fn aes256_cbc_decrypt( } let (iv, ciphertext) = if embeds_iv { let (iv, ciphertext) = ciphertext.split_at(16); - (*array_ref!(iv, 0, 16), ciphertext) + (array_ref!(iv, 0, 16), ciphertext) } else { - ([0u8; 16], ciphertext) + (&[0u8; 16], ciphertext) }; let mut plaintext = ciphertext.to_vec(); - let aes_dec_key = crypto::aes256::DecryptionKey::new(aes_enc_key); - cbc_decrypt(&aes_dec_key, iv, &mut plaintext); + aes_key.decrypt_cbc(iv, &mut plaintext); Ok(plaintext) } @@ -226,58 +225,63 @@ mod test { #[test] fn test_encrypt_decrypt_with_iv() { let mut env = TestEnv::default(); - let aes_enc_key = crypto::aes256::EncryptionKey::new(&[0xC2; 32]); + let aes_key = AesKey::::new(&[0xC2; 32]); let plaintext = vec![0xAA; 64]; - let ciphertext = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true).unwrap(); - let decrypted = aes256_cbc_decrypt(&aes_enc_key, &ciphertext, true).unwrap(); + let ciphertext = + aes256_cbc_encrypt::(env.rng(), &aes_key, &plaintext, true).unwrap(); + let decrypted = aes256_cbc_decrypt::(&aes_key, &ciphertext, true).unwrap(); assert_eq!(decrypted, plaintext); } #[test] fn test_encrypt_decrypt_without_iv() { let mut env = TestEnv::default(); - let aes_enc_key = crypto::aes256::EncryptionKey::new(&[0xC2; 32]); + let aes_key = AesKey::::new(&[0xC2; 32]); let plaintext = vec![0xAA; 64]; - let ciphertext = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, false).unwrap(); - let decrypted = aes256_cbc_decrypt(&aes_enc_key, &ciphertext, false).unwrap(); + let ciphertext = + aes256_cbc_encrypt::(env.rng(), &aes_key, &plaintext, false).unwrap(); + let decrypted = aes256_cbc_decrypt::(&aes_key, &ciphertext, false).unwrap(); assert_eq!(decrypted, plaintext); } #[test] fn test_correct_iv_usage() { let mut env = TestEnv::default(); - let aes_enc_key = crypto::aes256::EncryptionKey::new(&[0xC2; 32]); + let aes_key = AesKey::::new(&[0xC2; 32]); let plaintext = vec![0xAA; 64]; let mut ciphertext_no_iv = - aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, false).unwrap(); + aes256_cbc_encrypt::(env.rng(), &aes_key, &plaintext, false).unwrap(); let mut ciphertext_with_iv = vec![0u8; 16]; ciphertext_with_iv.append(&mut ciphertext_no_iv); - let decrypted = aes256_cbc_decrypt(&aes_enc_key, &ciphertext_with_iv, true).unwrap(); + let decrypted = aes256_cbc_decrypt::(&aes_key, &ciphertext_with_iv, true).unwrap(); assert_eq!(decrypted, plaintext); } #[test] fn test_iv_manipulation_property() { let mut env = TestEnv::default(); - let aes_enc_key = crypto::aes256::EncryptionKey::new(&[0xC2; 32]); + let aes_key = AesKey::::new(&[0xC2; 32]); let plaintext = vec![0xAA; 64]; - let mut ciphertext = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true).unwrap(); + let mut ciphertext = + aes256_cbc_encrypt::(env.rng(), &aes_key, &plaintext, true).unwrap(); let mut expected_plaintext = plaintext; for i in 0..16 { ciphertext[i] ^= 0xBB; expected_plaintext[i] ^= 0xBB; } - let decrypted = aes256_cbc_decrypt(&aes_enc_key, &ciphertext, true).unwrap(); + let decrypted = aes256_cbc_decrypt::(&aes_key, &ciphertext, true).unwrap(); assert_eq!(decrypted, expected_plaintext); } #[test] fn test_chaining() { let mut env = TestEnv::default(); - let aes_enc_key = crypto::aes256::EncryptionKey::new(&[0xC2; 32]); + let aes_key = AesKey::::new(&[0xC2; 32]); let plaintext = vec![0xAA; 64]; - let ciphertext1 = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true).unwrap(); - let ciphertext2 = aes256_cbc_encrypt(env.rng(), &aes_enc_key, &plaintext, true).unwrap(); + let ciphertext1 = + aes256_cbc_encrypt::(env.rng(), &aes_key, &plaintext, true).unwrap(); + let ciphertext2 = + aes256_cbc_encrypt::(env.rng(), &aes_key, &plaintext, true).unwrap(); assert_eq!(ciphertext1.len(), 80); assert_eq!(ciphertext2.len(), 80); // The ciphertext should mutate in all blocks with a different IV. diff --git a/libraries/opensk/src/ctap/pin_protocol.rs b/libraries/opensk/src/ctap/pin_protocol.rs index 19aea01..4194369 100644 --- a/libraries/opensk/src/ctap/pin_protocol.rs +++ b/libraries/opensk/src/ctap/pin_protocol.rs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +use crate::api::crypto::aes256::Aes256; use crate::api::crypto::ecdh::{PublicKey as _, SecretKey as _, SharedSecret as _}; use crate::api::crypto::hkdf256::Hkdf256; use crate::api::crypto::hmac256::Hmac256; @@ -22,9 +23,8 @@ use crate::ctap::data_formats::{CoseKey, PinUvAuthProtocol}; use crate::ctap::status_code::Ctap2StatusCode; #[cfg(test)] use crate::env::test::TestEnv; -use crate::env::{EcdhPk, EcdhSk, Env, Hkdf, Hmac, Sha}; +use crate::env::{AesKey, EcdhPk, EcdhSk, Env, Hkdf, Hmac, Sha}; use alloc::vec::Vec; -use core::marker::PhantomData; use rng256::Rng256; /// Implements common functions between existing PIN protocols for handshakes. @@ -207,29 +207,26 @@ fn verify_v2( pub struct SharedSecretV1 { common_secret: [u8; 32], - aes_enc_key: crypto::aes256::EncryptionKey, - // TODO: remove after porting AES to env crypto - phantom: PhantomData, + aes_key: AesKey, } impl SharedSecretV1 { /// Creates a new shared secret from the handshake result. fn new(handshake: [u8; 32]) -> Self { let common_secret = Sha::::digest(&handshake); - let aes_enc_key = crypto::aes256::EncryptionKey::new(&common_secret); + let aes_key = AesKey::::new(&common_secret); SharedSecretV1 { common_secret, - aes_enc_key, - phantom: PhantomData, + aes_key, } } fn encrypt(&self, rng: &mut dyn Rng256, plaintext: &[u8]) -> Result, Ctap2StatusCode> { - aes256_cbc_encrypt(rng, &self.aes_enc_key, plaintext, false) + aes256_cbc_encrypt::(rng, &self.aes_key, plaintext, false) } fn decrypt(&self, ciphertext: &[u8]) -> Result, Ctap2StatusCode> { - aes256_cbc_decrypt(&self.aes_enc_key, ciphertext, false) + aes256_cbc_decrypt::(&self.aes_key, ciphertext, false) } fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> { @@ -243,10 +240,8 @@ impl SharedSecretV1 { } pub struct SharedSecretV2 { - aes_enc_key: crypto::aes256::EncryptionKey, + aes_key: AesKey, hmac_key: [u8; 32], - // TODO: remove after porting AES to env crypto - phantom: PhantomData, } impl SharedSecretV2 { @@ -254,18 +249,17 @@ impl SharedSecretV2 { fn new(handshake: [u8; 32]) -> Self { let aes_key = Hkdf::::hkdf_empty_salt_256(&handshake, b"CTAP2 AES key"); SharedSecretV2 { - aes_enc_key: crypto::aes256::EncryptionKey::new(&aes_key), + aes_key: AesKey::::new(&aes_key), hmac_key: Hkdf::::hkdf_empty_salt_256(&handshake, b"CTAP2 HMAC key"), - phantom: PhantomData, } } fn encrypt(&self, rng: &mut dyn Rng256, plaintext: &[u8]) -> Result, Ctap2StatusCode> { - aes256_cbc_encrypt(rng, &self.aes_enc_key, plaintext, true) + aes256_cbc_encrypt::(rng, &self.aes_key, plaintext, true) } fn decrypt(&self, ciphertext: &[u8]) -> Result, Ctap2StatusCode> { - aes256_cbc_decrypt(&self.aes_enc_key, ciphertext, true) + aes256_cbc_decrypt::(&self.aes_key, ciphertext, true) } fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), Ctap2StatusCode> { diff --git a/libraries/opensk/src/env/mod.rs b/libraries/opensk/src/env/mod.rs index 9c86f23..1a37ceb 100644 --- a/libraries/opensk/src/env/mod.rs +++ b/libraries/opensk/src/env/mod.rs @@ -29,6 +29,7 @@ use rng256::Rng256; #[cfg(feature = "std")] pub mod test; +pub type AesKey = <::Crypto as Crypto>::Aes256; pub type EcdhSk = <<::Crypto as Crypto>::Ecdh as Ecdh>::SecretKey; pub type EcdhPk = <<::Crypto as Crypto>::Ecdh as Ecdh>::PublicKey; pub type EcdsaSk = <<::Crypto as Crypto>::Ecdsa as Ecdsa>::SecretKey;