Repository: google/OpenSK
File: src/ctap/crypto_wrapper.rs (Rust)

Code before:
///
/// This functions reads:
/// - legacy credentials (no version number),
/// - v1 (ECDSA)
/// - v2 (EdDSA over curve Ed25519)
pub fn decrypt_credential_source(
    env: &mut impl Env,
    credential_id: Vec<u8>,
    rp_id_hash: &[u8],
) -> Result<Option<PublicKeyCredentialSource>, Ctap2StatusCode> {
    if credential_id.len() < LEGACY_CREDENTIAL_ID_SIZE {
        return Ok(None);
    }
    let master_keys = storage::master_keys(env)?;
    let hmac_message_size = credential_id.len() - 32;
    if !verify_hmac_256::<Sha256>(
        &master_keys.hmac,
        &credential_id[..hmac_message_size],
        array_ref![credential_id, hmac_message_size, 32],
    ) {
        return Ok(None);
    }

    let algorithm;
    let payload = if credential_id.len() == LEGACY_CREDENTIAL_ID_SIZE {
        algorithm = ES256_ALGORITHM;
        &credential_id[..hmac_message_size]
    } else {
        // Version number check
        match credential_id[0] {
            ECDSA_CREDENTIAL_ID_VERSION => algorithm = ES256_ALGORITHM,
            #[cfg(feature = "with_ed25519")]
            ED25519_CREDENTIAL_ID_VERSION => algorithm = EDDSA_ALGORITHM,
            _ => return Ok(None),
        }
        &credential_id[1..hmac_message_size]
    };
    if payload.len() != 80 {
        // We shouldn't have HMAC'ed anything of different length. The check is cheap though.
        return Ok(None);
    }

    let aes_enc_key = crypto::aes256::EncryptionKey::new(&master_keys.encryption);
    let decrypted_id = aes256_cbc_decrypt(&aes_enc_key, payload, true)?;

    if rp_id_hash != &decrypted_id[32..] {
        return Ok(None);
    }
    let sk_option;
    match algorithm {
        ES256_ALGORITHM => sk_option = PrivateKey::new_ecdsa_from_bytes(&decrypted_id[..32]),

Review comment (security):
Again, I'd prefer to write: 
```
let (algorithm, payload) = if credential_id.len() == LEGACY_CREDENTIAL_ID_SIZE {
```

Code after:
            ED25519_CREDENTIAL_ID_VERSION => EDDSA_ALGORITHM,
            _ => return Ok(None),
        };
        (&credential_id[1..hmac_message_size], algorithm)
    };
    if payload.len() != 80 {
        // We shouldn't have HMAC'ed anything of different length. The check is cheap though.
        return Ok(None);
    }

    let aes_enc_key = crypto::aes256::EncryptionKey::new(&master_keys.encryption);
    let decrypted_id = aes256_cbc_decrypt(&aes_enc_key, payload, true)?;

    if rp_id_hash != &decrypted_id[32..] {
        return Ok(None);
    }
    let sk_option = match algorithm {
        ES256_ALGORITHM => PrivateKey::new_ecdsa_from_bytes(&decrypted_id[..32]),
        #[cfg(feature = "ed25519")]
        EDDSA_ALGORITHM => PrivateKey::new_ed25519_from_bytes(&decrypted_id[..32]),
        _ => return Ok(None),
    };

    Ok(sk_option.map(|sk| PublicKeyCredentialSource {
        key_type: PublicKeyCredentialType::PublicKey,
        credential_id,
        private_key: sk,
        rp_id: String::from(""),
        user_handle: vec![],
        user_display_name: None,
        cred_protect_policy: None,
        creation_order: 0,
        user_name: None,
        user_icon: None,
        cred_blob: None,
        large_blob_key: None,
    }))
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::env::test::TestEnv;

    #[test]
    fn test_encrypt_decrypt_with_iv() {
        let mut env = TestEnv::new();
        let aes_enc_key = crypto::aes256::EncryptionKey::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();