hopr_crypto_sphinx/
derivation.rs1use blake2::Blake2s256;
2use elliptic_curve::hash2curve::{ExpandMsgXmd, GroupDigest};
3use generic_array::{ArrayLength, GenericArray};
4use hkdf::SimpleHkdf;
5use hopr_crypto_random::random_fill;
6use hopr_crypto_types::errors::CryptoError::{CalculationError, InvalidParameterSize};
7use hopr_crypto_types::primitives::{DigestLike, SecretKey, SimpleDigest, SimpleMac};
8use hopr_crypto_types::types::{HalfKey, PacketTag};
9use k256::Secp256k1;
10
11const HASH_KEY_COMMITMENT_SEED: &str = "HASH_KEY_COMMITMENT_SEED";
13const HASH_KEY_HMAC: &str = "HASH_KEY_HMAC";
14const HASH_KEY_PACKET_TAG: &str = "HASH_KEY_PACKET_TAG";
15const HASH_KEY_OWN_KEY: &str = "HASH_KEY_OWN_KEY";
16const HASH_KEY_ACK_KEY: &str = "HASH_KEY_ACK_KEY";
17
18pub const PING_PONG_NONCE_SIZE: usize = 16;
20
21pub fn create_tagged_mac(secret: &SecretKey, data: &[u8]) -> [u8; SimpleMac::SIZE] {
25 let mut mac = SimpleMac::new(&derive_mac_key(secret));
26 mac.update(data);
27 mac.finalize().into()
28}
29
30fn hkdf_expand_from_prk<L: ArrayLength<u8>>(secret: &SecretKey, tag: &[u8]) -> GenericArray<u8, L> {
34 let hkdf = SimpleHkdf::<Blake2s256>::from_prk(secret.as_ref()).expect("size of the hkdf secret key is invalid"); let mut out = GenericArray::default();
39 hkdf.expand(tag, &mut out).expect("invalid hkdf output size"); out
42}
43
44pub fn derive_ping_pong(challenge: Option<&[u8]>) -> Box<[u8]> {
46 let mut ret = [0u8; PING_PONG_NONCE_SIZE];
47 match challenge {
48 None => random_fill(&mut ret),
49 Some(chal) => {
50 let mut digest = SimpleDigest::default();
51 digest.update(chal);
52 let hash = digest.finalize();
54 ret.copy_from_slice(&hash[0..PING_PONG_NONCE_SIZE]);
55 }
56 }
57 ret.into()
58}
59
60pub fn derive_commitment_seed(private_key: &[u8], channel_info: &[u8]) -> [u8; SimpleMac::SIZE] {
63 let sk: SecretKey = hkdf_expand_from_prk(
64 &private_key.try_into().expect("commitment private key size invalid"),
65 HASH_KEY_COMMITMENT_SEED.as_bytes(),
66 )
67 .into();
68 let mut mac = SimpleMac::new(&sk);
69 mac.update(channel_info);
70 mac.finalize().into()
71}
72
73pub fn derive_packet_tag(secret: &SecretKey) -> PacketTag {
75 hkdf_expand_from_prk::<typenum::U16>(secret, HASH_KEY_PACKET_TAG.as_bytes()).into()
76}
77
78pub fn derive_mac_key(secret: &SecretKey) -> SecretKey {
80 hkdf_expand_from_prk::<typenum::U32>(secret, HASH_KEY_HMAC.as_bytes()).into()
81}
82
83pub(crate) fn generate_key_iv(secret: &SecretKey, info: &[u8], key: &mut [u8], iv: &mut [u8], iv_first: bool) {
86 let hkdf = SimpleHkdf::<Blake2s256>::from_prk(secret.as_ref()).expect("secret key length must be correct");
87
88 let mut out = vec![0u8; key.len() + iv.len()];
89 hkdf.expand(info, &mut out)
90 .expect("key and iv are too big for this kdf");
91
92 if iv_first {
93 let (v_iv, v_key) = out.split_at(iv.len());
94 iv.copy_from_slice(v_iv);
95 key.copy_from_slice(v_key);
96 } else {
97 let (v_key, v_iv) = out.split_at(key.len());
98 key.copy_from_slice(v_key);
99 iv.copy_from_slice(v_iv);
100 }
101}
102
103pub fn sample_secp256k1_field_element(secret: &[u8], tag: &str) -> hopr_crypto_types::errors::Result<HalfKey> {
109 if secret.len() >= SecretKey::LENGTH {
110 let scalar = Secp256k1::hash_to_scalar::<ExpandMsgXmd<sha3::Sha3_256>>(
111 &[secret],
112 &[b"secp256k1_XMD:SHA3-256_SSWU_RO_", tag.as_bytes()],
113 )
114 .map_err(|_| CalculationError)?;
115 Ok(HalfKey::try_from(scalar.to_bytes().as_ref())?)
116 } else {
117 Err(InvalidParameterSize {
118 name: "secret".into(),
119 expected: SecretKey::LENGTH,
120 })
121 }
122}
123
124pub fn derive_own_key_share(secret: &SecretKey) -> HalfKey {
127 sample_secp256k1_field_element(secret.as_ref(), HASH_KEY_OWN_KEY).expect("failed to sample own key share")
128}
129
130pub fn derive_ack_key_share(secret: &SecretKey) -> HalfKey {
133 sample_secp256k1_field_element(secret.as_ref(), HASH_KEY_ACK_KEY).expect("failed to sample ack key share")
134}
135
136#[cfg(test)]
137mod tests {
138 use super::*;
139 use elliptic_curve::{sec1::ToEncodedPoint, ProjectivePoint, ScalarPrimitive};
140 use hex_literal::hex;
141 use hopr_crypto_types::keypairs::{ChainKeypair, Keypair};
142 use hopr_crypto_types::types::PublicKey;
143 use hopr_crypto_types::vrf::derive_vrf_parameters;
144 use k256::Scalar;
145
146 #[test]
147 fn test_derive_commitment_seed() {
148 let priv_key = [0u8; SecretKey::LENGTH];
149 let chinfo = [0u8; SecretKey::LENGTH];
150
151 let res = derive_commitment_seed(&priv_key, &chinfo);
152
153 let r = hex!("0abe559a1577e99e16f112bb8a88f7793ff1fb22af46b810995fb754ea319386");
154 assert_eq!(r, res.as_ref());
155 }
156
157 #[test]
158 fn test_derive_packet_tag() {
159 let tag = derive_packet_tag(&SecretKey::default());
160
161 let r = hex!("e0cf0fb82ea5a541b0367b376eb36a60");
162 assert_eq!(r, tag.as_ref());
163 }
164
165 #[test]
166 fn test_derive_mac_key() {
167 let tag = derive_mac_key(&SecretKey::default());
168
169 let r = hex!("7f656daaf7c2e64bcfc1386f8af273890e863dec63b410967a5652630617b09b");
170 assert_eq!(r, tag.as_ref());
171 }
172
173 #[test]
174 fn test_sample_field_element() {
175 let secret = [1u8; SecretKey::LENGTH];
176 assert!(sample_secp256k1_field_element(&secret, "TEST_TAG").is_ok());
177 }
178
179 #[test]
180 fn test_vrf_parameter_generation() -> anyhow::Result<()> {
181 let dst = b"some DST tag";
182 let priv_key: [u8; 32] = hex!("f13233ff60e1f618525dac5f7d117bef0bad0eb0b0afb2459f9cbc57a3a987ba"); let message = hex!("f13233ff60e1f618525dac5f7d117bef0bad0eb0b0afb2459f9cbc57a3a987ba"); let keypair = ChainKeypair::from_secret(&priv_key)?;
186 let pub_key = PublicKey::from_privkey(&priv_key)?;
188
189 let params = derive_vrf_parameters(&message, &keypair, dst)?;
190
191 let cap_b = Secp256k1::hash_from_bytes::<ExpandMsgXmd<sha3::Keccak256>>(
192 &[&pub_key.to_address().as_ref(), &message],
193 &[dst],
194 )?;
195
196 assert_eq!(
197 params.get_s_b_witness(&keypair.public().to_address(), &message, dst)?,
198 (cap_b * params.s).to_encoded_point(false)
199 );
200
201 let a: Scalar = ScalarPrimitive::<Secp256k1>::from_slice(&priv_key)?.into();
202 assert_eq!(params.get_h_v_witness(), (cap_b * a * params.h).to_encoded_point(false));
203
204 let r_v: ProjectivePoint<Secp256k1> = cap_b * params.s - params.V.clone().into_projective_point() * params.h;
205
206 let h_check = Secp256k1::hash_to_scalar::<ExpandMsgXmd<sha3::Keccak256>>(
207 &[
208 &pub_key.to_address().as_ref(),
209 ¶ms.V.as_uncompressed().as_bytes()[1..],
210 &r_v.to_affine().to_encoded_point(false).as_bytes()[1..],
211 &message,
212 ],
213 &[dst],
214 )?;
215
216 assert_eq!(h_check, params.h);
217
218 Ok(())
219 }
220
221 #[test]
222 fn test_mac() {
223 let key = GenericArray::from([1u8; SecretKey::LENGTH]);
224 let data = [2u8; 64];
225 let mac = create_tagged_mac(&key.into(), &data);
226
227 let expected = hex!("77264e8ea3052b621dbb8b1904403a64b1064c884cf7629c266edd7e237f2799");
228 assert_eq!(SimpleMac::SIZE, mac.len());
229 assert_eq!(expected, mac);
230 }
231}