use super::CryptoError; use aes::cipher::{BlockDecryptMut, BlockEncryptMut, KeyIvInit}; use hmac::Mac; use p256::elliptic_curve::sec1::FromEncodedPoint; use rand_core::RngCore; use sha2::Digest; use std::convert::TryInto; pub type Result = std::result::Result; fn cose_key_to_public(peer: &super::COSEEC2Key) -> Result { // SEC 1 encoded uncompressed point let peer = p256::EncodedPoint::from_affine_coordinates( peer.x .as_slice() .try_into() .map_err(|_| CryptoError::MalformedInput)?, peer.y .as_slice() .try_into() .map_err(|_| CryptoError::MalformedInput)?, false, ); p256::PublicKey::from_encoded_point(&peer) .into_option() .ok_or(CryptoError::LibraryFailure) } /// Ephemeral ECDH over P256. Generates an ephemeral P256 key pair. Returns /// 1) the x coordinate of the shared point, and /// 2) the uncompressed SEC 1 encoding of the ephemeral public key. pub fn ecdhe_p256_raw(peer: &super::COSEEC2Key) -> Result<(Vec, Vec)> { let peer_public = cose_key_to_public(peer)?; let internal_private = p256::ecdh::EphemeralSecret::random(&mut rand_core::OsRng); let internal_public = internal_private.public_key().to_sec1_bytes().into_vec(); let shared_point = internal_private.diffie_hellman(&peer_public); Ok((shared_point.raw_secret_bytes().to_vec(), internal_public)) } type Aes256CbcEnc = cbc::Encryptor; type Aes256CbcDec = cbc::Decryptor; const AES_BLOCK_SIZE: usize = 16; pub fn encrypt_aes_256_cbc_no_pad(key: &[u8], iv: Option<&[u8]>, data: &[u8]) -> Result> { let key: [u8; 32] = match key.try_into() { Ok(key) => key, Err(_) => return Err(CryptoError::LibraryFailure), }; let iv = iv.unwrap_or(&[0u8; AES_BLOCK_SIZE]); let iv = match iv.try_into() { Ok(iv) => iv, Err(_) => return Err(CryptoError::LibraryFailure), }; // Validate that the data is an exact multiple of the block size since we have no // padding available. let blocks = data.chunks_exact(AES_BLOCK_SIZE); if !blocks.remainder().is_empty() { return Err(CryptoError::LibraryFailure); } let mut encryptor = Aes256CbcEnc::new(&key.into(), iv); // Since we now know that `data` is a multiple of `AES_BLOCK_SIZE`, so this will always have the // same number of blocks as it. let mut ciphertext = vec![0u8; data.len()]; // XXX: `slice::as_chunks` would be better but it requires an MSRV of 1.88. for (input_block, output_block) in blocks .into_iter() .zip(ciphertext.chunks_exact_mut(AES_BLOCK_SIZE)) { let input: &[u8; AES_BLOCK_SIZE] = input_block.try_into().unwrap(); let output: &mut [u8; AES_BLOCK_SIZE] = output_block.try_into().unwrap(); encryptor.encrypt_block_b2b_mut(input.into(), output.into()); debug_assert_ne!(output, &[0u8; AES_BLOCK_SIZE]); } Ok(ciphertext) } pub fn decrypt_aes_256_cbc_no_pad(key: &[u8], iv: Option<&[u8]>, data: &[u8]) -> Result> { let key: [u8; 32] = match key.try_into() { Ok(key) => key, Err(_) => return Err(CryptoError::LibraryFailure), }; let iv = iv.unwrap_or(&[0u8; AES_BLOCK_SIZE]); let iv = match iv.try_into() { Ok(iv) => iv, Err(_) => return Err(CryptoError::LibraryFailure), }; // See comments in `encrypt_aes_256_cbc_no_pad` for rationale. let blocks = data.chunks_exact(AES_BLOCK_SIZE); if !blocks.remainder().is_empty() { return Err(CryptoError::LibraryFailure); } let mut decryptor = Aes256CbcDec::new(&key.into(), iv); let mut plaintext = vec![0u8; data.len()]; for (input_block, output_block) in blocks .into_iter() .zip(plaintext.chunks_exact_mut(AES_BLOCK_SIZE)) { let input: &[u8; AES_BLOCK_SIZE] = input_block.try_into().unwrap(); let output: &mut [u8; AES_BLOCK_SIZE] = output_block.try_into().unwrap(); decryptor.decrypt_block_b2b_mut(input.into(), output.into()); debug_assert_ne!(output, &[0u8; AES_BLOCK_SIZE]); } Ok(plaintext) } type HmacSha256 = hmac::Hmac; pub fn hmac_sha256(key: &[u8], data: &[u8]) -> Result> { let mut key = HmacSha256::new_from_slice(key) .map_err(|_| CryptoError::Backend(String::from("InvalidLength")))?; key.update(data); Ok(key.finalize().into_bytes().to_vec()) } pub fn sha256(data: &[u8]) -> Result> { let digest = sha2::Sha256::digest(data); Ok(digest.to_vec()) } pub fn random_bytes(count: usize) -> Result> { let mut rng = rand_core::OsRng; let mut out = vec![0u8; count]; rng.try_fill_bytes(out.as_mut_slice()) .map_err(|_| CryptoError::LibraryFailure)?; Ok(out) } #[cfg(test)] pub fn test_ecdh_p256_raw( peer: &super::COSEEC2Key, _client_public_x: &[u8], _client_public_y: &[u8], client_private: &[u8], ) -> Result> { let peer_public = cose_key_to_public(peer)?; let client_private = p256::SecretKey::from_slice(client_private).unwrap(); let shared_point = p256::ecdh::diffie_hellman(client_private.to_nonzero_scalar(), peer_public.as_affine()); Ok(shared_point.raw_secret_bytes().to_vec()) } pub fn gen_p256() -> Result<(Vec, Vec)> { unimplemented!() } pub fn ecdsa_p256_sha256_sign_raw(_private: &[u8], _data: &[u8]) -> Result> { unimplemented!() } #[allow(dead_code)] #[cfg(test)] pub fn test_ecdsa_p256_sha256_verify_raw( _public: &[u8], _signature: &[u8], _data: &[u8], ) -> Result<()> { unimplemented!() }