Add test implementations of the Crypto and Dice traits

Change-Id: Ifda7b5a4b04555b364ee36c51649a30f4f147341
Reviewed-on: https://pigweed-review.googlesource.com/c/open-dice/+/220232
Reviewed-by: Andrew Scull <ascull@google.com>
Lint: Lint 🤖 <android-build-ayeaye@system.gserviceaccount.com>
Commit-Queue: Darren Krahn <dkrahn@google.com>
Reviewed-by: Marshall Pierce <marshallpierce@google.com>
diff --git a/dpe-rs/src/commands_test.rs b/dpe-rs/src/commands_test.rs
index aabedd1..3a8e0e8 100644
--- a/dpe-rs/src/commands_test.rs
+++ b/dpe-rs/src/commands_test.rs
@@ -17,13 +17,11 @@
 
 use crate::args::{ArgMap, ArgMapExt, ArgTypeMap, ArgTypeSelector};
 use crate::commands::{handle_command_message, DeriveContextOptions, DpeCore};
-use crate::crypto::Hash;
 use crate::crypto::{
     HandshakeMessage, SealingPublicKey, Signature, SigningPublicKey,
 };
-use crate::dice::{Certificate, DiceInput, InternalInputType};
 use crate::dice::{
-    DiceInputAuthority, DiceInputCode, DiceInputConfig, DiceInputMode,
+    test::get_fake_dice_input, Certificate, DiceInput, InternalInputType,
 };
 use crate::encode::{CommandSelector, ContextHandle, LocalityId, SessionId};
 use crate::encode_test::{
@@ -838,17 +836,6 @@
     }
 }
 
-fn get_fake_dice_input<'a>() -> DiceInput<'a> {
-    let hash = Hash::from_slice(&[0; 64]).unwrap();
-    DiceInput {
-        code: DiceInputCode::CodeHash(hash.clone()),
-        config: DiceInputConfig::ConfigInlineValue(hash.clone()),
-        authority: Some(DiceInputAuthority::AuthorityHash(hash.clone())),
-        mode: DiceInputMode::NotInitialized,
-        hidden: Some(hash.clone()),
-    }
-}
-
 #[test]
 #[allow(unused_results)]
 fn fake_commands() {
diff --git a/dpe-rs/src/crypto.rs b/dpe-rs/src/crypto.rs
index 4dc6e98..b127a6d 100644
--- a/dpe-rs/src/crypto.rs
+++ b/dpe-rs/src/crypto.rs
@@ -319,3 +319,223 @@
         in_place_buffer: &mut Message,
     ) -> DpeResult<()>;
 }
+
+#[cfg(test)]
+pub(crate) mod test {
+    use super::*;
+    use crate::cbor::{
+        cbor_decoder_from_message, cbor_encoder_from_message,
+        encode_bytes_prefix, DecoderExt,
+    };
+    use crate::error::ErrCode;
+    use crate::memory::SmallMessage;
+    use crate::noise::test::SessionCryptoForTesting;
+    use aes_gcm_siv::{AeadInPlace, Aes256GcmSiv, KeyInit};
+    use ed25519_dalek::Signer;
+    use hkdf::Hkdf;
+    use hmac::{Hmac, Mac};
+    use hpke::{
+        aead::AeadTag, aead::AesGcm256, kdf::HkdfSha512, kem::Kem,
+        kem::X25519HkdfSha256, Deserializable, OpModeR, OpModeS, Serializable,
+    };
+    use log::{debug, error};
+    use rand_chacha::ChaCha12Rng;
+    use rand_core::SeedableRng;
+    use sha2::{Digest, Sha512};
+
+    pub(crate) type HmacSha512 = Hmac<Sha512>;
+
+    impl From<aes_gcm_siv::Error> for ErrCode {
+        fn from(err: aes_gcm_siv::Error) -> Self {
+            error!("Decrypt error: {:?}", err);
+            ErrCode::InvalidArgument
+        }
+    }
+
+    impl From<hpke::HpkeError> for ErrCode {
+        fn from(err: hpke::HpkeError) -> Self {
+            error!("Hpke error: {:?}", err);
+            ErrCode::InvalidArgument
+        }
+    }
+
+    #[derive(Clone, Default, Debug, Eq, PartialEq, Hash)]
+    pub(crate) struct CryptoForTesting {
+        pub(crate) noise: SessionCryptoForTesting,
+    }
+
+    impl Crypto for CryptoForTesting {
+        type S = SessionCryptoForTesting;
+
+        fn hash(input: &[u8]) -> Hash {
+            Hash::from_slice(Sha512::digest(input).as_slice()).unwrap()
+        }
+
+        fn hash_iter<'a>(iter: impl Iterator<Item = &'a [u8]>) -> Hash {
+            let mut hasher = Sha512::new();
+            for input in iter {
+                hasher.update(input);
+            }
+            Hash::from_slice(hasher.finalize().as_slice()).unwrap()
+        }
+
+        fn kdf(
+            kdf_ikm: &[u8],
+            kdf_info: &[u8],
+            kdf_salt: &[u8],
+            derived_key: &mut [u8],
+        ) -> DpeResult<()> {
+            Hkdf::<Sha512>::new(Some(kdf_salt), kdf_ikm)
+                .expand(kdf_info, derived_key)
+                .unwrap();
+            Ok(())
+        }
+
+        fn signing_keypair_from_seed(
+            seed: &Hash,
+        ) -> DpeResult<(SigningPublicKey, SigningPrivateKey)> {
+            let mut key_bytes: ed25519_dalek::SecretKey = Default::default();
+            key_bytes.copy_from_slice(&seed.as_slice()[..32]);
+            let sk = ed25519_dalek::SigningKey::from_bytes(&key_bytes);
+            Ok((
+                SigningPublicKey::from_array(sk.verifying_key().as_bytes()),
+                SigningPrivateKey::from_array(sk.as_bytes()),
+            ))
+        }
+
+        fn sealing_keypair_from_seed(
+            seed: &Hash,
+        ) -> DpeResult<(SealingPublicKey, SealingPrivateKey)> {
+            let (private_key, public_key) =
+                <X25519HkdfSha256 as Kem>::derive_keypair(seed.as_slice());
+            Ok((
+                SealingPublicKey::from_slice(public_key.to_bytes().as_slice())?,
+                SealingPrivateKey::from_slice(
+                    private_key.to_bytes().as_slice(),
+                )?,
+            ))
+        }
+
+        fn mac(key: &MacKey, data: &[u8]) -> DpeResult<Hash> {
+            let mut hmac =
+                <HmacSha512 as Mac>::new_from_slice(key.as_slice()).unwrap();
+            hmac.update(data);
+            Ok(Hash::from_slice(hmac.finalize().into_bytes().as_slice())
+                .unwrap())
+        }
+
+        fn sign(key: &SigningPrivateKey, tbs: &[u8]) -> DpeResult<Signature> {
+            let sk = ed25519_dalek::SigningKey::from_bytes(key.as_array());
+            let sig = sk.sign(tbs).to_bytes();
+            Ok(Signature::from_slice(&sig).unwrap())
+        }
+
+        fn seal(
+            key: &EncryptionKey,
+            in_place_buffer: &mut Message,
+        ) -> DpeResult<()> {
+            const TAG_LENGTH: usize = 16;
+            let cipher = Aes256GcmSiv::new(key.as_slice().into());
+            let nonce = Default::default();
+            // Make space to append the tag.
+            let required_buffer_size = in_place_buffer.len() + TAG_LENGTH;
+            in_place_buffer
+                .vec
+                .resize_default(required_buffer_size)
+                .map_err(|_| ErrCode::OutOfMemory)
+                .unwrap();
+            cipher
+                .encrypt_in_place(&nonce, &[], &mut in_place_buffer.vec)
+                .unwrap();
+            Ok(())
+        }
+
+        fn unseal(
+            key: &EncryptionKey,
+            in_place_buffer: &mut Message,
+        ) -> DpeResult<()> {
+            const TAG_LENGTH: usize = 16;
+            let cipher = Aes256GcmSiv::new(key.as_slice().into());
+            let nonce = Default::default();
+            cipher.decrypt_in_place(&nonce, &[], &mut in_place_buffer.vec)?;
+            let plaintext_len = in_place_buffer.len() - TAG_LENGTH;
+            in_place_buffer.vec.truncate(plaintext_len);
+            Ok(())
+        }
+
+        fn seal_asymmetric(
+            public_key: &SealingPublicKey,
+            in_place_buffer: &mut Message,
+        ) -> DpeResult<()> {
+            let mut rng = <ChaCha12Rng as SeedableRng>::from_seed([0xFF; 32]);
+            let kem_public_key =
+                <X25519HkdfSha256 as Kem>::PublicKey::from_bytes(
+                    public_key.as_slice(),
+                )?;
+            let (encapped_key, aead_tag) =
+                hpke::single_shot_seal_in_place_detached::<
+                    AesGcm256,
+                    HkdfSha512,
+                    X25519HkdfSha256,
+                    _,
+                >(
+                    &OpModeS::Base,
+                    &kem_public_key,
+                    &[],
+                    in_place_buffer.vec.as_mut(),
+                    &[],
+                    &mut rng,
+                )?;
+            let mut prefix = SmallMessage::new();
+            let mut encoder = cbor_encoder_from_message(&mut prefix);
+            let _ = encoder.bytes(encapped_key.to_bytes().as_slice())?;
+            let _ = encoder.bytes(aead_tag.to_bytes().as_slice())?;
+            encode_bytes_prefix(&mut prefix, in_place_buffer.len())?;
+            debug!(
+                "seal_asymmetric: h={}, d={}",
+                prefix.len(),
+                in_place_buffer.len()
+            );
+            in_place_buffer.insert_prefix(prefix.as_slice())?;
+            Ok(())
+        }
+
+        fn unseal_asymmetric(
+            private_key: &SealingPrivateKey,
+            in_place_buffer: &mut Message,
+        ) -> DpeResult<()> {
+            let mut decoder = cbor_decoder_from_message(in_place_buffer);
+            let encapped_key =
+                <X25519HkdfSha256 as Kem>::EncappedKey::from_bytes(
+                    decoder.bytes()?,
+                )?;
+            let tag = AeadTag::from_bytes(decoder.bytes()?)?;
+            // Leave only the ciphertext in in_place_buffer.
+            let sealed_data_position = decoder.decode_bytes_prefix()?;
+            debug!(
+                "unseal_asymmetric: h={}, d={}",
+                sealed_data_position,
+                in_place_buffer.len() - sealed_data_position
+            );
+            in_place_buffer.remove_prefix(sealed_data_position)?;
+            let kem_private_key =
+                <X25519HkdfSha256 as Kem>::PrivateKey::from_bytes(
+                    private_key.as_slice(),
+                )?;
+            hpke::single_shot_open_in_place_detached::<
+                AesGcm256,
+                HkdfSha512,
+                X25519HkdfSha256,
+            >(
+                &OpModeR::Base,
+                &kem_private_key,
+                &encapped_key,
+                &[],
+                in_place_buffer.vec.as_mut(),
+                &[],
+                &tag,
+            )?;
+            Ok(())
+        }
+    }
+}
diff --git a/dpe-rs/src/dice.rs b/dpe-rs/src/dice.rs
index 2264d37..a113402 100644
--- a/dpe-rs/src/dice.rs
+++ b/dpe-rs/src/dice.rs
@@ -207,3 +207,272 @@
         additional_input: &[u8],
     ) -> DpeResult<Certificate>;
 }
+
+#[cfg(test)]
+pub(crate) mod test {
+    use super::*;
+    use crate::constants::SIGNING_PUBLIC_KEY_SIZE;
+    use crate::crypto::test::CryptoForTesting;
+    use crate::crypto::Crypto;
+    use crate::error::ErrCode;
+
+    #[derive(Default)]
+    pub(crate) struct DiceForTesting;
+    impl Dice for DiceForTesting {
+        fn dice(
+            &self,
+            cdi_sign: &Cdi,
+            cdi_seal: &Cdi,
+            // Note: this implementation is only useful for testing, it doesn't
+            // actually use the input values.
+            _inputs: &DiceInput,
+            _internal_inputs: &[InternalInputType],
+            is_export: bool,
+        ) -> DpeResult<(Cdi, Cdi)> {
+            let export_tag = if is_export { "export".as_bytes() } else { &[] };
+            let new_cdi_sign = Cdi::from_slice(
+                &CryptoForTesting::hash_iter(
+                    [cdi_sign.as_slice(), export_tag].into_iter(),
+                )
+                .as_slice()[0..DICE_CDI_SIZE],
+            )?;
+            let new_cdi_seal = Cdi::from_slice(
+                &CryptoForTesting::hash_iter(
+                    [cdi_seal.as_slice(), export_tag].into_iter(),
+                )
+                .as_slice()[0..DICE_CDI_SIZE],
+            )?;
+            Ok((new_cdi_sign, new_cdi_seal))
+        }
+
+        fn derive_eca_key_pair(
+            &self,
+            cdi_sign: &Cdi,
+        ) -> DpeResult<(SigningPublicKey, SigningPrivateKey)> {
+            let kdf_info = CryptoForTesting::hash(&[]);
+            let kdf_salt =
+                CryptoForTesting::hash("Key_Pair_25519_ECA".as_bytes());
+            let kdf_ikm = cdi_sign.as_slice();
+            let mut seed: Hash = Default::default();
+            CryptoForTesting::kdf(
+                kdf_ikm,
+                &kdf_info.as_slice(),
+                &kdf_salt.as_slice(),
+                seed.as_mut_slice(),
+            )?;
+            CryptoForTesting::signing_keypair_from_seed(&seed)
+        }
+
+        fn derive_signing_key_pair(
+            &self,
+            cdi_sign: &Cdi,
+            label: &[u8],
+        ) -> DpeResult<(SigningPublicKey, SigningPrivateKey)> {
+            let kdf_info = CryptoForTesting::hash(label);
+            let kdf_salt =
+                CryptoForTesting::hash("Key_Pair_25519_Sign".as_bytes());
+            let kdf_ikm = cdi_sign.as_slice();
+            let mut seed: Hash = Default::default();
+            CryptoForTesting::kdf(
+                kdf_ikm,
+                &kdf_info.as_slice(),
+                &kdf_salt.as_slice(),
+                seed.as_mut_slice(),
+            )?;
+            CryptoForTesting::signing_keypair_from_seed(&seed)
+        }
+
+        fn derive_sealing_key_pair(
+            &self,
+            cdi_seal: &Cdi,
+            label: &[u8],
+            unseal_policy: &[u8],
+        ) -> DpeResult<(SealingPublicKey, SealingPrivateKey)> {
+            let kdf_info =
+                CryptoForTesting::hash_iter([label, unseal_policy].into_iter());
+            let kdf_salt =
+                CryptoForTesting::hash("Key_Pair_25519_Seal".as_bytes());
+            let kdf_ikm = cdi_seal.as_slice();
+            let mut seed: Hash = Default::default();
+            CryptoForTesting::kdf(
+                kdf_ikm,
+                &kdf_info.as_slice(),
+                &kdf_salt.as_slice(),
+                seed.as_mut_slice(),
+            )?;
+            CryptoForTesting::sealing_keypair_from_seed(&seed)
+        }
+
+        fn derive_mac_key(
+            &self,
+            cdi_sign: &Cdi,
+            label: &[u8],
+        ) -> DpeResult<MacKey> {
+            let kdf_info = CryptoForTesting::hash(label);
+            let kdf_salt = CryptoForTesting::hash("Key_HMAC_Sign".as_bytes());
+            let kdf_ikm = cdi_sign.as_slice();
+            let mut key: MacKey = Default::default();
+            CryptoForTesting::kdf(
+                kdf_ikm,
+                &kdf_info.as_slice(),
+                &kdf_salt.as_slice(),
+                key.as_mut_slice(),
+            )?;
+            Ok(key)
+        }
+
+        fn derive_sealing_key(
+            &self,
+            cdi_seal: &Cdi,
+            label: &[u8],
+            unseal_policy: &[u8],
+        ) -> DpeResult<EncryptionKey> {
+            let kdf_info =
+                CryptoForTesting::hash_iter([label, unseal_policy].into_iter());
+            let kdf_salt = CryptoForTesting::hash("Key_AES_Seal".as_bytes());
+            let kdf_ikm = cdi_seal.as_slice();
+            let mut key: EncryptionKey = Default::default();
+            CryptoForTesting::kdf(
+                kdf_ikm,
+                &kdf_info.as_slice(),
+                &kdf_salt.as_slice(),
+                key.as_mut_slice(),
+            )?;
+            Ok(key)
+        }
+
+        fn create_certificate_info(
+            &self,
+            inputs: &DiceInput,
+            _internal_inputs: &[InternalInputType],
+        ) -> DpeResult<CertificateInfo> {
+            let mut info: CertificateInfo = Default::default();
+            let content = match &inputs.code {
+                DiceInputCode::CodeHash(h) => h.as_slice(),
+                DiceInputCode::CodeDescriptor(d) => d,
+            };
+            info.0
+                .extend_from_slice(content)
+                .map_err(|_| ErrCode::OutOfMemory)
+                .unwrap();
+            Ok(info)
+        }
+
+        fn create_eca_certificate(
+            &self,
+            issuer_key_pair: &(SigningPublicKey, SigningPrivateKey),
+            subject_public_key: &SigningPublicKey,
+            certificate_info: &CertificateInfo,
+            additional_certificate_info: &CertificateInfoList,
+            _is_export: bool,
+        ) -> DpeResult<Certificate> {
+            let mut new_eca_certificate: Certificate = Default::default();
+            new_eca_certificate
+                .0
+                .extend_from_slice(issuer_key_pair.0.as_slice())
+                .unwrap();
+            new_eca_certificate
+                .0
+                .extend_from_slice(subject_public_key.as_slice())
+                .unwrap();
+            new_eca_certificate
+                .0
+                .extend_from_slice(certificate_info.0.as_slice())
+                .unwrap();
+            for info in &additional_certificate_info.0 {
+                new_eca_certificate
+                    .0
+                    .extend_from_slice(info.0.as_slice())
+                    .unwrap();
+            }
+            let signature = CryptoForTesting::sign(
+                &issuer_key_pair.1,
+                new_eca_certificate.0.as_slice(),
+            )?;
+            new_eca_certificate
+                .0
+                .extend_from_slice(signature.as_slice())
+                .unwrap();
+            Ok(new_eca_certificate)
+        }
+
+        fn create_leaf_certificate(
+            &self,
+            issuer_key_pair: &(SigningPublicKey, SigningPrivateKey),
+            subject_public_key: &SigningPublicKey,
+            certificate_info: &CertificateInfoList,
+            additional_input: &[u8],
+        ) -> DpeResult<Certificate> {
+            let mut new_leaf_certificate: Certificate = Default::default();
+            new_leaf_certificate
+                .0
+                .extend_from_slice(issuer_key_pair.0.as_slice())
+                .unwrap();
+            new_leaf_certificate
+                .0
+                .extend_from_slice(subject_public_key.as_slice())
+                .unwrap();
+            new_leaf_certificate.0.extend_from_slice(additional_input).unwrap();
+            for info in &certificate_info.0 {
+                new_leaf_certificate
+                    .0
+                    .extend_from_slice(info.0.as_slice())
+                    .unwrap();
+            }
+            let signature = CryptoForTesting::sign(
+                &issuer_key_pair.1,
+                new_leaf_certificate.0.as_slice(),
+            )?;
+            new_leaf_certificate
+                .0
+                .extend_from_slice(signature.as_slice())
+                .unwrap();
+            Ok(new_leaf_certificate)
+        }
+    }
+
+    pub(crate) fn get_fake_dice_input() -> DiceInput<'static> {
+        let hash = Hash::from_slice(&[0; 64]).unwrap();
+        DiceInput {
+            code: DiceInputCode::CodeHash(hash.clone()),
+            config: DiceInputConfig::ConfigInlineValue(hash.clone()),
+            authority: Some(DiceInputAuthority::AuthorityHash(hash.clone())),
+            mode: DiceInputMode::NotInitialized,
+            hidden: Some(hash.clone()),
+        }
+    }
+
+    // Checks fake certs as constructed by DiceForTesting methods
+    pub(crate) fn check_cert(
+        cert: &Certificate,
+        expected_issuer: Option<&SigningPublicKey>,
+        expected_subject: &SigningPublicKey,
+        expected_cert_info: &CertificateInfoList,
+        expected_additional_info: &[u8],
+    ) -> () {
+        if let Some(issuer) = expected_issuer {
+            assert!(cert.0.starts_with(issuer.as_slice()));
+            let (tbs, sig) =
+                cert.0.split_at(cert.0.len() - SIGNING_PUBLIC_KEY_SIZE * 2);
+            let key = ed25519_dalek::VerifyingKey::try_from(issuer.as_slice())
+                .unwrap();
+            key.verify_strict(
+                tbs,
+                &ed25519_dalek::Signature::try_from(sig).unwrap(),
+            )
+            .unwrap();
+        }
+        assert!(cert.0[SIGNING_PUBLIC_KEY_SIZE..]
+            .starts_with(expected_subject.as_slice()));
+        assert!(cert.0[SIGNING_PUBLIC_KEY_SIZE * 2..]
+            .starts_with(expected_additional_info));
+        let mut cert_slice = &cert.0
+            [SIGNING_PUBLIC_KEY_SIZE * 2 + expected_additional_info.len()..];
+        for info in &expected_cert_info.0 {
+            assert!(cert_slice.starts_with(info.0.as_slice()));
+            cert_slice = &cert_slice[info.0.len()..];
+        }
+        // Only the signature is left
+        assert!(cert_slice.len() == SIGNING_PUBLIC_KEY_SIZE * 2);
+    }
+}
diff --git a/dpe-rs/src/noise.rs b/dpe-rs/src/noise.rs
index 7eaa89f..5f4f70c 100644
--- a/dpe-rs/src/noise.rs
+++ b/dpe-rs/src/noise.rs
@@ -522,21 +522,23 @@
 }
 
 #[cfg(test)]
-mod tests {
+pub(crate) mod test {
     use super::*;
 
-    struct DepsForTesting {}
+    pub(crate) struct DepsForTesting {}
     impl NoiseCryptoDeps for DepsForTesting {
         type Cipher = noise_rust_crypto::Aes256Gcm;
         type DH = noise_rust_crypto::X25519;
         type Hash = noise_rust_crypto::Sha512;
     }
 
-    type SessionCryptoForTesting = NoiseSessionCrypto<DepsForTesting>;
+    pub(crate) type SessionCryptoForTesting =
+        NoiseSessionCrypto<DepsForTesting>;
 
-    type SessionClientForTesting = SessionClient<DepsForTesting>;
+    pub(crate) type SessionClientForTesting = SessionClient<DepsForTesting>;
 
-    type CipherStateForTesting = NoiseCipherState<noise_rust_crypto::Aes256Gcm>;
+    pub(crate) type CipherStateForTesting =
+        NoiseCipherState<noise_rust_crypto::Aes256Gcm>;
 
     #[test]
     fn end_to_end_session() {