1use crate::constants::{
16 BLINDING_FACTOR_SIZE, EXPANDED_SHARED_SECRET_HKDF_INFO, EXPANDED_SHARED_SECRET_HKDF_SALT,
17 EXPANDED_SHARED_SECRET_LENGTH, INTEGRITY_MAC_KEY_SIZE, PAYLOAD_KEY_SEED_SIZE, PAYLOAD_KEY_SIZE,
18 REPLAY_TAG_SIZE,
19};
20use crate::crypto::STREAM_CIPHER_KEY_SIZE;
21use crate::header::keys::{HeaderIntegrityMacKey, StreamCipherKey};
22use crate::header::SphinxHeader;
23use crate::payload::key::PayloadKey;
24use arrayref::array_ref;
25use hkdf::Hkdf;
26use sha2::Sha256;
27use x25519_dalek::{PublicKey, SharedSecret, StaticSecret};
28use zeroize::{Zeroize, ZeroizeOnDrop};
29
30pub(crate) trait ExpandSecret {
31 fn expand_shared_secret(&self) -> ExpandedSharedSecret;
32}
33
34impl ExpandSecret for PublicKey {
35 fn expand_shared_secret(&self) -> ExpandedSharedSecret {
36 self.as_bytes().expand_shared_secret()
37 }
38}
39
40impl ExpandSecret for SharedSecret {
41 fn expand_shared_secret(&self) -> ExpandedSharedSecret {
42 self.as_bytes().expand_shared_secret()
43 }
44}
45
46impl ExpandSecret for [u8; 32] {
47 fn expand_shared_secret(&self) -> ExpandedSharedSecret {
48 expand_shared_secret(self)
49 }
50}
51
52#[derive(Zeroize, ZeroizeOnDrop, Clone, PartialEq, Debug)]
53pub struct ExpandedSharedSecret([u8; EXPANDED_SHARED_SECRET_LENGTH]);
54
55impl ExpandedSharedSecret {
56 pub(crate) fn stream_cipher_key(&self) -> &StreamCipherKey {
61 array_ref!(&self.0, 0, STREAM_CIPHER_KEY_SIZE)
62 }
63
64 pub(crate) fn header_integrity_hmac_key(&self) -> &HeaderIntegrityMacKey {
66 array_ref!(&self.0, STREAM_CIPHER_KEY_SIZE, INTEGRITY_MAC_KEY_SIZE)
67 }
68
69 pub(crate) fn legacy_payload_key(&self) -> &PayloadKey {
72 array_ref!(
73 &self.0,
74 STREAM_CIPHER_KEY_SIZE + INTEGRITY_MAC_KEY_SIZE,
75 PAYLOAD_KEY_SIZE
76 )
77 }
78
79 pub(crate) fn payload_key_seed(&self) -> &[u8; PAYLOAD_KEY_SEED_SIZE] {
81 array_ref!(
82 &self.0,
83 STREAM_CIPHER_KEY_SIZE + INTEGRITY_MAC_KEY_SIZE,
84 PAYLOAD_KEY_SEED_SIZE
85 )
86 }
87
88 pub(crate) fn blinding_factor_bytes(&self) -> &[u8; BLINDING_FACTOR_SIZE] {
90 array_ref!(
91 &self.0,
92 STREAM_CIPHER_KEY_SIZE + INTEGRITY_MAC_KEY_SIZE + PAYLOAD_KEY_SIZE,
93 BLINDING_FACTOR_SIZE
94 )
95 }
96
97 pub(crate) fn blinding_factor(&self) -> StaticSecret {
98 StaticSecret::from(*self.blinding_factor_bytes())
99 }
100
101 pub(crate) fn blind_shared_secret(&self, shared_secret: PublicKey) -> PublicKey {
102 SphinxHeader::blind_the_shared_secret(shared_secret, self.blinding_factor())
103 }
104
105 #[deprecated]
106 #[allow(deprecated)]
107 pub(crate) fn legacy_blind_share_secret(&self, shared_secret: PublicKey) -> PublicKey {
108 SphinxHeader::legacy_blind_shared_secret(shared_secret, self.blinding_factor())
109 }
110
111 pub fn replay_tag(&self) -> &[u8; REPLAY_TAG_SIZE] {
113 array_ref!(
114 &self.0,
115 STREAM_CIPHER_KEY_SIZE
116 + INTEGRITY_MAC_KEY_SIZE
117 + PAYLOAD_KEY_SIZE
118 + BLINDING_FACTOR_SIZE,
119 REPLAY_TAG_SIZE
120 )
121 }
122}
123
124pub(crate) fn expand_shared_secret(shared_secret: &[u8; 32]) -> ExpandedSharedSecret {
125 let hkdf = Hkdf::<Sha256>::new(Some(EXPANDED_SHARED_SECRET_HKDF_SALT), shared_secret);
126
127 let mut output = [0u8; EXPANDED_SHARED_SECRET_LENGTH];
128 #[allow(clippy::unwrap_used)]
130 hkdf.expand(EXPANDED_SHARED_SECRET_HKDF_INFO, &mut output)
131 .unwrap();
132
133 ExpandedSharedSecret(output)
134}
135
136#[cfg(test)]
137mod expanding_shared_secret {
138 use super::*;
139 use crate::test_utils::fixtures::mock_shared_secret;
140 use crate::test_utils::{assert_zeroize, assert_zeroize_on_drop, seeded_rng};
141
142 #[test]
143 fn expanded_shared_secret_is_zeroized() {
144 assert_zeroize::<ExpandedSharedSecret>();
145 assert_zeroize_on_drop::<ExpandedSharedSecret>();
146 }
147
148 #[test]
150 fn results_in_same_values_as_old_implementation() {
151 let mut rng = seeded_rng([1u8; 32]);
152 let ss = mock_shared_secret(&mut rng);
153
154 let expected_sck = [
155 186, 234, 152, 113, 202, 124, 191, 228, 173, 89, 91, 8, 127, 251, 214, 200,
156 ];
157 let expected_hihk = [
158 28, 222, 17, 227, 46, 180, 170, 7, 34, 52, 177, 142, 150, 137, 142, 222,
159 ];
160 let expected_pk = [
161 210, 209, 81, 241, 254, 123, 36, 81, 155, 85, 115, 40, 101, 210, 97, 8, 196, 104, 61,
162 23, 165, 190, 191, 236, 203, 69, 15, 230, 70, 100, 161, 136, 53, 88, 116, 118, 81, 57,
163 58, 181, 232, 102, 149, 93, 239, 255, 156, 205, 0, 146, 110, 117, 137, 59, 102, 170,
164 87, 250, 175, 207, 193, 107, 112, 154, 247, 220, 110, 135, 32, 106, 20, 152, 14, 132,
165 89, 154, 249, 24, 176, 40, 30, 182, 195, 209, 124, 59, 58, 201, 209, 255, 80, 151, 109,
166 226, 157, 232, 48, 128, 56, 159, 90, 168, 229, 60, 106, 14, 50, 215, 198, 200, 168, 24,
167 159, 224, 240, 119, 23, 242, 61, 129, 54, 36, 140, 245, 127, 159, 230, 5, 52, 142, 254,
168 52, 168, 171, 139, 100, 206, 16, 94, 219, 68, 113, 141, 159, 4, 233, 189, 144, 164,
169 202, 180, 74, 214, 66, 96, 185, 70, 191, 155, 18, 210, 52, 123, 71, 231, 225, 79, 0,
170 196, 25, 217, 231, 133, 191, 96, 119, 103, 182, 200, 36, 215, 62, 203, 149, 214, 139,
171 32, 70, 66, 30, 63, 102,
172 ];
173 let expected_bf = [
174 227, 64, 184, 235, 140, 62, 232, 172, 235, 42, 58, 169, 241, 253, 245, 2, 136, 149, 74,
175 48, 6, 165, 145, 133, 190, 105, 222, 218, 248, 172, 49, 188,
176 ];
177
178 let expanded = expand_shared_secret(ss.as_bytes());
179 assert_eq!(expanded.stream_cipher_key(), &expected_sck);
180 assert_eq!(expanded.header_integrity_hmac_key(), &expected_hihk);
181 assert_eq!(expanded.legacy_payload_key(), &expected_pk);
182 assert_eq!(expanded.blinding_factor_bytes(), &expected_bf);
183 }
184}