askar_crypto/kdf/
ecdh_es.rs

1//! ECDH-ES key derivation
2
3use sha2::Sha256;
4use zeroize::Zeroize;
5
6use super::{
7    concat::{ConcatKDFHash, ConcatKDFParams},
8    KeyDerivation, KeyExchange,
9};
10use crate::error::Error;
11
12/// An instantiation of the ECDH-ES key derivation
13#[derive(Debug)]
14pub struct EcdhEs<'d, Key>
15where
16    Key: KeyExchange + ?Sized,
17{
18    ephem_key: &'d Key,
19    recip_key: &'d Key,
20    alg: &'d [u8],
21    apu: &'d [u8],
22    apv: &'d [u8],
23    receive: bool,
24}
25
26impl<'d, Key: KeyExchange + ?Sized> EcdhEs<'d, Key> {
27    /// Create a new KDF instance
28    pub fn new(
29        ephem_key: &'d Key,
30        recip_key: &'d Key,
31        alg: &'d [u8],
32        apu: &'d [u8],
33        apv: &'d [u8],
34        receive: bool,
35    ) -> Self {
36        Self {
37            ephem_key,
38            recip_key,
39            alg,
40            apu,
41            apv,
42            receive,
43        }
44    }
45}
46
47impl<Key: KeyExchange + ?Sized> KeyDerivation for EcdhEs<'_, Key> {
48    fn derive_key_bytes(&mut self, key_output: &mut [u8]) -> Result<(), Error> {
49        let output_len = key_output.len();
50        // one-pass KDF only produces 256 bits of output
51        if output_len > 32 {
52            return Err(err_msg!(Unsupported, "Exceeded maximum output length"));
53        }
54        let mut kdf = ConcatKDFHash::<Sha256>::new();
55        kdf.start_pass();
56
57        // hash Z directly into the KDF
58        if self.receive {
59            self.recip_key
60                .write_key_exchange(self.ephem_key, &mut kdf)?;
61        } else {
62            self.ephem_key
63                .write_key_exchange(self.recip_key, &mut kdf)?;
64        }
65
66        kdf.hash_params(ConcatKDFParams {
67            alg: self.alg,
68            apu: self.apu,
69            apv: self.apv,
70            pub_info: &((output_len as u32) * 8).to_be_bytes(), // output length in bits
71            prv_info: &[],
72        });
73
74        let mut key = kdf.finish_pass();
75        key_output.copy_from_slice(&key[..output_len]);
76        key.zeroize();
77
78        Ok(())
79    }
80}
81
82#[cfg(test)]
83mod tests {
84    #[allow(unused_imports)]
85    use super::*;
86
87    #[cfg(feature = "ed25519")]
88    #[test]
89    // based on RFC sample keys
90    // https://tools.ietf.org/html/rfc8037#appendix-A.6
91    fn expected_es_direct_output() {
92        use crate::alg::x25519::X25519KeyPair;
93        use crate::jwk::FromJwk;
94
95        let bob_pk = X25519KeyPair::from_jwk(
96            r#"{"kty":"OKP","crv":"X25519","kid":"Bob",
97            "x":"3p7bfXt9wbTTW2HC7OQ1Nz-DQ8hbeGdNrfx-FG-IK08"}"#,
98        )
99        .unwrap();
100        let ephem_sk = X25519KeyPair::from_jwk(
101            r#"{"kty":"OKP","crv":"X25519",
102            "d":"dwdtCnMYpX08FsFyUbJmRd9ML4frwJkqsXf7pR25LCo",
103            "x":"hSDwCYkwp1R0i33ctD73Wg2_Og0mOBr066SpjqqbTmo"}
104         "#,
105        )
106        .unwrap();
107
108        let xk = ephem_sk.key_exchange_bytes(&bob_pk).unwrap();
109        assert_eq!(
110            xk,
111            &hex!("4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742")[..]
112        );
113
114        let mut key_output = [0u8; 32];
115
116        EcdhEs::new(&ephem_sk, &bob_pk, b"A256GCM", b"Alice", b"Bob", false)
117            .derive_key_bytes(&mut key_output)
118            .unwrap();
119
120        assert_eq!(
121            key_output,
122            hex!("2f3636918ddb57fe0b3569113f19c4b6c518c2843f8930f05db25cd55dee53c1")
123        );
124    }
125}