hopr_crypto_sphinx/
derivation.rs

1use hopr_crypto_types::prelude::*;
2
3// Module-specific constants
4const HASH_KEY_PACKET_TAG: &str = "HASH_KEY_PACKET_TAG";
5
6pub(crate) fn create_kdf_instance<S: AsRef<[u8]>>(
7    secret: &S,
8    context: &str,
9    salt: Option<&[u8]>,
10) -> hopr_crypto_types::errors::Result<Blake3Output> {
11    let key_material = secret.as_ref();
12    if key_material.len() < 16 {
13        return Err(CryptoError::InvalidInputValue("secret must have at least 128-bits"));
14    }
15
16    if let Some(salt) = salt {
17        Ok(Blake3::new_derive_key(context)
18            .update_reader(salt)
19            .map_err(|_| CryptoError::InvalidInputValue("salt"))?
20            .update_reader(key_material)
21            .map_err(|_| CryptoError::InvalidInputValue("key"))?
22            .finalize_xof())
23    } else {
24        Ok(Blake3::new_derive_key(context)
25            .update_reader(key_material)
26            .map_err(|_| CryptoError::InvalidInputValue("key"))?
27            .finalize_xof())
28    }
29}
30
31/// Derives the packet tag used during packet construction by expanding the given secret.
32pub fn derive_packet_tag(secret: &SecretKey) -> hopr_crypto_types::errors::Result<PacketTag> {
33    let mut packet_tag: PacketTag = [0u8; PACKET_TAG_LENGTH];
34
35    let mut output = create_kdf_instance(secret, HASH_KEY_PACKET_TAG, None)?;
36    output.fill(&mut packet_tag);
37    Ok(packet_tag)
38}
39
40/// Internal convenience function to generate key and IV from the given secret,
41/// that is cryptographically strong.
42///
43/// The `secret` must be at least 16 bytes long.
44/// The function internally uses Blake2s256 based HKDF (see RFC 5869).
45///
46/// For `extract_with_salt` is given, the HKDF uses `Extract` with the given salt first
47/// and then calls `Expand` to derive the key and IV.
48///
49/// Otherwise, only `Expand` is used to derive key and IV using the given `info`, but
50/// the secret size must be exactly 32 bytes.
51pub(crate) fn generate_key<T: crypto_traits::KeyInit, S: AsRef<[u8]>>(
52    secret: &S,
53    context: &str,
54    with_salt: Option<&[u8]>,
55) -> hopr_crypto_types::errors::Result<T> {
56    let mut out = crypto_traits::Key::<T>::default();
57
58    let mut output = create_kdf_instance(secret, context, with_salt)?;
59    output.fill(&mut out);
60
61    Ok(T::new(&out))
62}
63
64/// Internal convenience function to generate key and IV from the given secret,
65/// that is cryptographically strong.
66///
67/// See [`generate_key`] for details.
68pub(crate) fn generate_key_iv<T: crypto_traits::KeyIvInit, S: AsRef<[u8]>>(
69    secret: &S,
70    context: &str,
71    with_salt: Option<&[u8]>,
72) -> hopr_crypto_types::errors::Result<T> {
73    let mut output = create_kdf_instance(secret, context, with_salt)?;
74
75    let mut key = crypto_traits::Key::<T>::default();
76    let mut iv = crypto_traits::Iv::<T>::default();
77
78    let mut out = vec![0u8; key.len() + iv.len()];
79    output.fill(&mut out);
80
81    let (v_iv, v_key) = out.split_at(iv.len());
82    iv.copy_from_slice(v_iv);
83    key.copy_from_slice(v_key);
84
85    Ok(T::new(&key, &iv))
86}
87
88#[cfg(test)]
89mod tests {
90    use super::*;
91    use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
92    use elliptic_curve::{sec1::ToEncodedPoint, ProjectivePoint, ScalarPrimitive};
93    use hex_literal::hex;
94    use hopr_crypto_types::keypairs::{ChainKeypair, Keypair};
95    use hopr_crypto_types::types::PublicKey;
96    use hopr_crypto_types::vrf::derive_vrf_parameters;
97    use k256::{Scalar, Secp256k1};
98
99    #[test]
100    fn test_sample_field_element() {
101        let secret = [1u8; SecretKey::LENGTH];
102        assert!(sample_secp256k1_field_element(&secret, "TEST_TAG").is_ok());
103    }
104
105    #[test]
106    fn test_vrf_parameter_generation() -> anyhow::Result<()> {
107        let dst = b"some DST tag";
108        let priv_key: [u8; 32] = hex!("f13233ff60e1f618525dac5f7d117bef0bad0eb0b0afb2459f9cbc57a3a987ba"); // dummy
109        let message = hex!("f13233ff60e1f618525dac5f7d117bef0bad0eb0b0afb2459f9cbc57a3a987ba"); // dummy
110
111        let keypair = ChainKeypair::from_secret(&priv_key)?;
112        // vrf verification algorithm
113        let pub_key = PublicKey::from_privkey(&priv_key)?;
114
115        let params = derive_vrf_parameters(&message, &keypair, dst)?;
116
117        let cap_b =
118            Secp256k1::hash_from_bytes::<ExpandMsgXmd<Keccak256>>(&[&pub_key.to_address().as_ref(), &message], &[dst])?;
119
120        assert_eq!(
121            params.get_s_b_witness(&keypair.public().to_address(), &message, dst)?,
122            (cap_b * params.s).to_encoded_point(false)
123        );
124
125        let a: Scalar = ScalarPrimitive::<Secp256k1>::from_slice(&priv_key)?.into();
126        assert_eq!(params.get_h_v_witness(), (cap_b * a * params.h).to_encoded_point(false));
127
128        let r_v: ProjectivePoint<Secp256k1> = cap_b * params.s - params.V.clone().into_projective_point() * params.h;
129
130        let h_check = Secp256k1::hash_to_scalar::<ExpandMsgXmd<Keccak256>>(
131            &[
132                &pub_key.to_address().as_ref(),
133                &params.V.as_uncompressed().as_bytes()[1..],
134                &r_v.to_affine().to_encoded_point(false).as_bytes()[1..],
135                &message,
136            ],
137            &[dst],
138        )?;
139
140        assert_eq!(h_check, params.h);
141
142        Ok(())
143    }
144}