1use x25519_dalek::{PublicKey, StaticSecret};
21
22use crate::kdf::derive_hkdf_sha256;
23
24pub fn pairwise_kek(
27 my_x25519_secret: &[u8; 32],
28 peer_x25519_public: &[u8; 32],
29 info: &[u8],
30) -> [u8; 32] {
31 let secret = StaticSecret::from(*my_x25519_secret);
32 let peer = PublicKey::from(*peer_x25519_public);
33 let shared = secret.diffie_hellman(&peer);
34 derive_hkdf_sha256(shared.as_bytes(), b"crypt-pairwise-v1", info)
35}
36
37#[cfg(test)]
38mod tests {
39 use super::*;
40 use crate::identity::{Keypair, PublicKey as IdPublicKey};
41
42 fn roundtrip_pair() -> (Keypair, Keypair) {
43 (
44 Keypair::from_seed(&[1u8; 32]),
45 Keypair::from_seed(&[2u8; 32]),
46 )
47 }
48
49 #[test]
50 fn ecdh_is_symmetric() {
51 let (alice, bob) = roundtrip_pair();
52 let kek_a = pairwise_kek(
53 &alice.to_x25519_secret_bytes(),
54 &bob.public_key().to_x25519_public_bytes(),
55 b"zone:default",
56 );
57 let kek_b = pairwise_kek(
58 &bob.to_x25519_secret_bytes(),
59 &alice.public_key().to_x25519_public_bytes(),
60 b"zone:default",
61 );
62 assert_eq!(kek_a, kek_b);
63 }
64
65 #[test]
66 fn third_party_gets_different_kek() {
67 let (alice, bob) = roundtrip_pair();
68 let eve = Keypair::from_seed(&[9u8; 32]);
69 let kek_ab = pairwise_kek(
70 &alice.to_x25519_secret_bytes(),
71 &bob.public_key().to_x25519_public_bytes(),
72 b"zone:default",
73 );
74 let kek_eb = pairwise_kek(
75 &eve.to_x25519_secret_bytes(),
76 &bob.public_key().to_x25519_public_bytes(),
77 b"zone:default",
78 );
79 assert_ne!(kek_ab, kek_eb);
80 }
81
82 #[test]
83 fn distinct_zones_get_distinct_keks() {
84 let (alice, bob) = roundtrip_pair();
85 let kek_default = pairwise_kek(
86 &alice.to_x25519_secret_bytes(),
87 &bob.public_key().to_x25519_public_bytes(),
88 b"zone:default",
89 );
90 let kek_other = pairwise_kek(
91 &alice.to_x25519_secret_bytes(),
92 &bob.public_key().to_x25519_public_bytes(),
93 b"zone:customer-x",
94 );
95 assert_ne!(kek_default, kek_other);
96 }
97
98 #[test]
99 fn self_wrap_is_deterministic() {
100 let alice = Keypair::from_seed(&[5u8; 32]);
101 let pk = alice.public_key();
102 let a = pairwise_kek(
103 &alice.to_x25519_secret_bytes(),
104 &pk.to_x25519_public_bytes(),
105 b"zone:default",
106 );
107 let b = pairwise_kek(
108 &alice.to_x25519_secret_bytes(),
109 &pk.to_x25519_public_bytes(),
110 b"zone:default",
111 );
112 assert_eq!(a, b);
113 }
114
115 #[test]
116 fn id_public_key_helper_matches_keypair() {
117 let kp = Keypair::from_seed(&[3u8; 32]);
120 let pk_hex = kp.public_key().to_hex();
121 let parsed = IdPublicKey::from_hex(&pk_hex).unwrap();
122 assert_eq!(
123 parsed.to_x25519_public_bytes(),
124 kp.public_key().to_x25519_public_bytes()
125 );
126 }
127}