1use crate::field::Fe;
4use ic_core::ct::Choice;
5use ic_core::traits::{Algorithm, KeyAgreement, SelfTest};
6use ic_core::{ensure, Result};
7
8const BASEPOINT: [u8; 32] = {
10 let mut b = [0u8; 32];
11 b[0] = 9;
12 b
13};
14
15pub struct X25519;
17
18impl Algorithm for X25519 {
19 const ID: &'static str = "x25519";
20 const NAME: &'static str = "X25519";
21}
22
23fn clamp(scalar: &[u8], out: &mut [u8; 32]) {
28 out.copy_from_slice(&scalar[..32]);
29 out[0] &= 248;
30 out[31] &= 127;
31 out[31] |= 64;
32}
33
34fn ladder(scalar: &[u8; 32], u: &[u8; 32]) -> [u8; 32] {
39 let x1 = Fe::from_bytes(u);
40 let mut x2 = Fe::ONE;
41 let mut z2 = Fe::ZERO;
42 let mut x3 = x1;
43 let mut z3 = Fe::ONE;
44 let mut swap = Choice::FALSE;
45
46 for pos in (0..255).rev() {
47 let bit = Choice::from_u8((scalar[pos / 8] >> (pos & 7)) & 1);
48 swap = Choice::from_u8(swap.unwrap_u8() ^ bit.unwrap_u8());
49 Fe::cswap(&mut x2, &mut x3, swap);
50 Fe::cswap(&mut z2, &mut z3, swap);
51 swap = bit;
52
53 let a = x2.add(&z2);
54 let b = x2.sub(&z2);
55 let aa = a.square();
56 let bb = b.square();
57 let e = aa.sub(&bb);
58 let c = x3.add(&z3);
59 let d = x3.sub(&z3);
60 let da = d.mul(&a);
61 let cb = c.mul(&b);
62
63 x3 = da.add(&cb).square();
64 z3 = x1.mul(&da.sub(&cb).square());
65 x2 = aa.mul(&bb);
66 z2 = e.mul(&bb.add(&e.mul121666()));
70 }
71
72 Fe::cswap(&mut x2, &mut x3, swap);
73 Fe::cswap(&mut z2, &mut z3, swap);
74 x2.mul(&z2.invert()).to_bytes()
75}
76
77impl KeyAgreement for X25519 {
78 const PRIVATE_KEY_LEN: usize = 32;
79 const PUBLIC_KEY_LEN: usize = 32;
80 const SHARED_SECRET_LEN: usize = 32;
81
82 fn public_key(private_key: &[u8], out: &mut [u8]) -> Result<()> {
83 ic_core::module::operational()?;
84 ensure!(private_key.len() == 32, InvalidLength, "x25519 private key");
85 ensure!(out.len() == 32, InvalidLength, "x25519 public key buffer");
86 let mut k = [0u8; 32];
87 clamp(private_key, &mut k);
88 out.copy_from_slice(&ladder(&k, &BASEPOINT));
89 Ok(())
90 }
91
92 fn agree(private_key: &[u8], peer_public_key: &[u8], out: &mut [u8]) -> Result<()> {
93 ic_core::module::operational()?;
94 ensure!(private_key.len() == 32, InvalidLength, "x25519 private key");
95 ensure!(
96 peer_public_key.len() == 32,
97 InvalidLength,
98 "x25519 public key"
99 );
100 ensure!(
101 out.len() == 32,
102 InvalidLength,
103 "x25519 shared secret buffer"
104 );
105
106 let mut k = [0u8; 32];
107 clamp(private_key, &mut k);
108 let mut u = [0u8; 32];
109 u.copy_from_slice(peer_public_key);
110
111 let shared = ladder(&k, &u);
112
113 ensure!(
118 !bool::from(ic_core::ct::is_zero(&shared)),
119 InvalidParameter,
120 "x25519 peer public key has small order"
121 );
122 out.copy_from_slice(&shared);
123 Ok(())
124 }
125}
126
127impl SelfTest for X25519 {
128 fn self_test() -> Result<()> {
129 let mut sk = [0u8; 32];
131 ic_core::codec::hex_decode(
132 b"77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a",
133 &mut sk,
134 )?;
135 let mut want = [0u8; 32];
136 ic_core::codec::hex_decode(
137 b"8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a",
138 &mut want,
139 )?;
140 let mut got = [0u8; 32];
141 <Self as KeyAgreement>::public_key(&sk, &mut got)?;
142 ensure!(ic_core::ct::verify(&want, &got), SelfTestFailed, "x25519");
143 Ok(())
144 }
145}
146
147#[cfg(test)]
148mod tests {
149 use super::*;
150 use ic_core::codec::{hex, unhex};
151
152 #[test]
154 fn rfc7748_scalar_mult_vector_1() {
155 let k = unhex("a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4").unwrap();
156 let u = unhex("e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c").unwrap();
157 let mut sk = [0u8; 32];
158 clamp(&k, &mut sk);
159 let mut up = [0u8; 32];
160 up.copy_from_slice(&u);
161 assert_eq!(
162 hex(&ladder(&sk, &up)),
163 "c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552"
164 );
165 }
166
167 #[test]
169 fn rfc7748_scalar_mult_vector_2() {
170 let k = unhex("4b66e9d4d1b4673c5ad22691957d6af5c11b6421e0ea01d42ca4169e7918ba0d").unwrap();
171 let u = unhex("e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a493").unwrap();
172 let mut sk = [0u8; 32];
173 clamp(&k, &mut sk);
174 let mut up = [0u8; 32];
175 up.copy_from_slice(&u);
176 assert_eq!(
177 hex(&ladder(&sk, &up)),
178 "95cbde9476e8907d7aade45cb4b873f88b595a68799fa152e6f8f7647aac7957"
179 );
180 }
181
182 #[test]
184 fn rfc7748_diffie_hellman() {
185 let a_sk =
186 unhex("77076d0a7318a57d3c16c17251b26645df4c2f87ebc0992ab177fba51db92c2a").unwrap();
187 let b_sk =
188 unhex("5dab087e624a8a4b79e17f8b83800ee66f3bb1292618b6fd1c2f8b27ff88e0eb").unwrap();
189
190 let mut a_pk = [0u8; 32];
191 let mut b_pk = [0u8; 32];
192 X25519::public_key(&a_sk, &mut a_pk).unwrap();
193 X25519::public_key(&b_sk, &mut b_pk).unwrap();
194 assert_eq!(
195 hex(&a_pk),
196 "8520f0098930a754748b7ddcb43ef75a0dbf3a0d26381af4eba4a98eaa9b4e6a"
197 );
198 assert_eq!(
199 hex(&b_pk),
200 "de9edb7d7b7dc1b4d35b61c2ece435373f8343c85b78674dadfc7e146f882b4f"
201 );
202
203 let mut s1 = [0u8; 32];
204 let mut s2 = [0u8; 32];
205 X25519::agree(&a_sk, &b_pk, &mut s1).unwrap();
206 X25519::agree(&b_sk, &a_pk, &mut s2).unwrap();
207 assert_eq!(s1, s2);
208 assert_eq!(
209 hex(&s1),
210 "4a5d9d5ba4ce2de1728e3bf480350f25e07e21c947d19e3376f09b3c1e161742"
211 );
212 }
213
214 #[test]
215 fn rejects_small_order_points() {
216 let sk = [0x11u8; 32];
217 let mut out = [0u8; 32];
218 for bad in [
220 "0000000000000000000000000000000000000000000000000000000000000000",
221 "0100000000000000000000000000000000000000000000000000000000000000",
222 "e0eb7a7c3b41b8ae1656e3faf19fc46ada098deb9c32b1fd866205165f49b800",
223 "5f9c95bca3508c24b1d0b1559c83ef5b04445cc4581c8e86d8224eddd09f1157",
224 "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
225 ] {
226 let pk = unhex(bad).unwrap();
227 assert!(
228 X25519::agree(&sk, &pk, &mut out).is_err(),
229 "small-order point {bad} must be rejected"
230 );
231 }
232 }
233
234 #[test]
235 fn rejects_wrong_lengths() {
236 let mut out = [0u8; 32];
237 assert!(X25519::public_key(&[0u8; 31], &mut out).is_err());
238 assert!(X25519::public_key(&[0u8; 32], &mut out[..31]).is_err());
239 assert!(X25519::agree(&[1u8; 32], &[0u8; 31], &mut out).is_err());
240 }
241
242 #[test]
245 fn clamping_is_applied_to_caller_scalars() {
246 let mut a = [0x42u8; 32];
247 let mut b = a;
248 a[0] |= 0x07;
249 b[0] &= !0x07;
250 a[31] |= 0x80;
251 b[31] &= 0x7f;
252
253 let mut pa = [0u8; 32];
254 let mut pb = [0u8; 32];
255 X25519::public_key(&a, &mut pa).unwrap();
256 X25519::public_key(&b, &mut pb).unwrap();
257 assert_eq!(pa, pb);
258 }
259
260 #[test]
261 fn self_test_passes() {
262 X25519::self_test().unwrap();
263 }
264}