Skip to content

Commit 50c4f85

Browse files
committed
Refactoring EC marshalling; support agreement::PrivateKey
1 parent 0853ae1 commit 50c4f85

File tree

8 files changed

+727
-300
lines changed

8 files changed

+727
-300
lines changed

aws-lc-rs/src/agreement.rs

Lines changed: 334 additions & 255 deletions
Large diffs are not rendered by default.

aws-lc-rs/src/agreement/ephemeral.rs

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,352 @@ where
111111
{
112112
agree(&my_private_key.0, peer_public_key, error_value, kdf)
113113
}
114+
115+
#[cfg(test)]
116+
mod tests {
117+
use crate::error::Unspecified;
118+
use crate::{agreement, rand, test, test_file};
119+
120+
#[test]
121+
fn test_agreement_ecdh_x25519_rfc_iterated() {
122+
fn expect_iterated_x25519(
123+
expected_result: &str,
124+
range: core::ops::Range<usize>,
125+
k: &mut Vec<u8>,
126+
u: &mut Vec<u8>,
127+
) {
128+
for _ in range {
129+
let new_k = x25519(k, u);
130+
*u = k.clone();
131+
*k = new_k;
132+
}
133+
assert_eq!(&h(expected_result), k);
134+
}
135+
136+
let mut k = h("0900000000000000000000000000000000000000000000000000000000000000");
137+
let mut u = k.clone();
138+
139+
expect_iterated_x25519(
140+
"422c8e7a6227d7bca1350b3e2bb7279f7897b87bb6854b783c60e80311ae3079",
141+
0..1,
142+
&mut k,
143+
&mut u,
144+
);
145+
expect_iterated_x25519(
146+
"684cf59ba83309552800ef566f2f4d3c1c3887c49360e3875f2eb94d99532c51",
147+
1..1_000,
148+
&mut k,
149+
&mut u,
150+
);
151+
152+
// The spec gives a test vector for 1,000,000 iterations but it takes
153+
// too long to do 1,000,000 iterations by default right now. This
154+
// 10,000 iteration vector is self-computed.
155+
expect_iterated_x25519(
156+
"2c125a20f639d504a7703d2e223c79a79de48c4ee8c23379aa19a62ecd211815",
157+
1_000..10_000,
158+
&mut k,
159+
&mut u,
160+
);
161+
162+
if cfg!(feature = "slow_tests") {
163+
expect_iterated_x25519(
164+
"7c3911e0ab2586fd864497297e575e6f3bc601c0883c30df5f4dd2d24f665424",
165+
10_000..1_000_000,
166+
&mut k,
167+
&mut u,
168+
);
169+
}
170+
}
171+
172+
#[test]
173+
fn test_agreement_x25519() {
174+
let alg = &agreement::X25519;
175+
let peer_public = agreement::UnparsedPublicKey::new(
176+
alg,
177+
test::from_dirty_hex(
178+
"e6db6867583030db3594c1a424b15f7c726624ec26b3353b10a903a6d0ab1c4c",
179+
),
180+
);
181+
182+
let my_private = test::from_dirty_hex(
183+
"a546e36bf0527c9d3b16154b82465edd62144c0ac1fc5a18506a2244ba449ac4",
184+
);
185+
186+
let my_private = {
187+
let rng = test::rand::FixedSliceRandom { bytes: &my_private };
188+
agreement::EphemeralPrivateKey::generate_for_test(alg, &rng).unwrap()
189+
};
190+
191+
let my_public = test::from_dirty_hex(
192+
"1c9fd88f45606d932a80c71824ae151d15d73e77de38e8e000852e614fae7019",
193+
);
194+
let output = test::from_dirty_hex(
195+
"c3da55379de9c6908e94ea4df28d084f32eccf03491c71f754b4075577a28552",
196+
);
197+
198+
assert_eq!(my_private.algorithm(), alg);
199+
200+
let computed_public = my_private.compute_public_key().unwrap();
201+
assert_eq!(computed_public.as_ref(), &my_public[..]);
202+
203+
assert_eq!(computed_public.algorithm(), alg);
204+
205+
let result = agreement::agree_ephemeral(my_private, &peer_public, (), |key_material| {
206+
assert_eq!(key_material, &output[..]);
207+
Ok(())
208+
});
209+
assert_eq!(result, Ok(()));
210+
}
211+
212+
#[test]
213+
fn test_agreement_ecdh_p256() {
214+
let alg = &agreement::ECDH_P256;
215+
let peer_public = agreement::UnparsedPublicKey::new(
216+
alg,
217+
test::from_dirty_hex(
218+
"04D12DFB5289C8D4F81208B70270398C342296970A0BCCB74C736FC7554494BF6356FBF3CA366CC23E8157854C13C58D6AAC23F046ADA30F8353E74F33039872AB",
219+
),
220+
);
221+
assert_eq!(peer_public.algorithm(), alg);
222+
assert_eq!(peer_public.bytes(), &peer_public.bytes);
223+
224+
let my_private = test::from_dirty_hex(
225+
"C88F01F510D9AC3F70A292DAA2316DE544E9AAB8AFE84049C62A9C57862D1433",
226+
);
227+
228+
let my_private = {
229+
let rng = test::rand::FixedSliceRandom { bytes: &my_private };
230+
agreement::EphemeralPrivateKey::generate_for_test(alg, &rng).unwrap()
231+
};
232+
233+
let my_public = test::from_dirty_hex(
234+
"04DAD0B65394221CF9B051E1FECA5787D098DFE637FC90B9EF945D0C37725811805271A0461CDB8252D61F1C456FA3E59AB1F45B33ACCF5F58389E0577B8990BB3",
235+
);
236+
let output = test::from_dirty_hex(
237+
"D6840F6B42F6EDAFD13116E0E12565202FEF8E9ECE7DCE03812464D04B9442DE",
238+
);
239+
240+
assert_eq!(my_private.algorithm(), alg);
241+
242+
let computed_public = my_private.compute_public_key().unwrap();
243+
assert_eq!(computed_public.as_ref(), &my_public[..]);
244+
245+
assert_eq!(computed_public.algorithm(), alg);
246+
247+
let result = agreement::agree_ephemeral(my_private, &peer_public, (), |key_material| {
248+
assert_eq!(key_material, &output[..]);
249+
Ok(())
250+
});
251+
assert_eq!(result, Ok(()));
252+
}
253+
254+
#[test]
255+
fn test_agreement_ecdh_p384() {
256+
let alg = &agreement::ECDH_P384;
257+
let peer_public = agreement::UnparsedPublicKey::new(
258+
alg,
259+
test::from_dirty_hex(
260+
"04E558DBEF53EECDE3D3FCCFC1AEA08A89A987475D12FD950D83CFA41732BC509D0D1AC43A0336DEF96FDA41D0774A3571DCFBEC7AACF3196472169E838430367F66EEBE3C6E70C416DD5F0C68759DD1FFF83FA40142209DFF5EAAD96DB9E6386C",
261+
),
262+
);
263+
264+
let my_private = test::from_dirty_hex(
265+
"099F3C7034D4A2C699884D73A375A67F7624EF7C6B3C0F160647B67414DCE655E35B538041E649EE3FAEF896783AB194",
266+
);
267+
268+
let my_private = {
269+
let rng = test::rand::FixedSliceRandom { bytes: &my_private };
270+
agreement::EphemeralPrivateKey::generate_for_test(alg, &rng).unwrap()
271+
};
272+
273+
let my_public = test::from_dirty_hex(
274+
"04667842D7D180AC2CDE6F74F37551F55755C7645C20EF73E31634FE72B4C55EE6DE3AC808ACB4BDB4C88732AEE95F41AA9482ED1FC0EEB9CAFC4984625CCFC23F65032149E0E144ADA024181535A0F38EEB9FCFF3C2C947DAE69B4C634573A81C",
275+
);
276+
let output = test::from_dirty_hex(
277+
"11187331C279962D93D604243FD592CB9D0A926F422E47187521287E7156C5C4D603135569B9E9D09CF5D4A270F59746",
278+
);
279+
280+
assert_eq!(my_private.algorithm(), alg);
281+
282+
let computed_public = my_private.compute_public_key().unwrap();
283+
assert_eq!(computed_public.as_ref(), &my_public[..]);
284+
285+
assert_eq!(computed_public.algorithm(), alg);
286+
287+
let result = agreement::agree_ephemeral(my_private, &peer_public, (), |key_material| {
288+
assert_eq!(key_material, &output[..]);
289+
Ok(())
290+
});
291+
assert_eq!(result, Ok(()));
292+
}
293+
294+
#[test]
295+
fn test_agreement_ecdh_p521() {
296+
let alg = &agreement::ECDH_P521;
297+
let peer_public = agreement::UnparsedPublicKey::new(
298+
alg,
299+
test::from_dirty_hex(
300+
"0401a32099b02c0bd85371f60b0dd20890e6c7af048c8179890fda308b359dbbc2b7a832bb8c6526c4af99a7ea3f0b3cb96ae1eb7684132795c478ad6f962e4a6f446d017627357b39e9d7632a1370b3e93c1afb5c851b910eb4ead0c9d387df67cde85003e0e427552f1cd09059aad0262e235cce5fba8cedc4fdc1463da76dcd4b6d1a46",
301+
),
302+
);
303+
304+
let my_private = test::from_dirty_hex(
305+
"00df14b1f1432a7b0fb053965fd8643afee26b2451ecb6a8a53a655d5fbe16e4c64ce8647225eb11e7fdcb23627471dffc5c2523bd2ae89957cba3a57a23933e5a78",
306+
);
307+
308+
let my_private = {
309+
let rng = test::rand::FixedSliceRandom { bytes: &my_private };
310+
agreement::EphemeralPrivateKey::generate_for_test(alg, &rng).unwrap()
311+
};
312+
313+
let my_public = test::from_dirty_hex(
314+
"04004e8583bbbb2ecd93f0714c332dff5ab3bc6396e62f3c560229664329baa5138c3bb1c36428abd4e23d17fcb7a2cfcc224b2e734c8941f6f121722d7b6b9415457601cf0874f204b0363f020864672fadbf87c8811eb147758b254b74b14fae742159f0f671a018212bbf25b8519e126d4cad778cfff50d288fd39ceb0cac635b175ec0",
315+
);
316+
let output = test::from_dirty_hex(
317+
"01aaf24e5d47e4080c18c55ea35581cd8da30f1a079565045d2008d51b12d0abb4411cda7a0785b15d149ed301a3697062f42da237aa7f07e0af3fd00eb1800d9c41",
318+
);
319+
320+
assert_eq!(my_private.algorithm(), alg);
321+
322+
let computed_public = my_private.compute_public_key().unwrap();
323+
assert_eq!(computed_public.as_ref(), &my_public[..]);
324+
325+
assert_eq!(computed_public.algorithm(), alg);
326+
327+
let result = agreement::agree_ephemeral(my_private, &peer_public, (), |key_material| {
328+
assert_eq!(key_material, &output[..]);
329+
Ok(())
330+
});
331+
assert_eq!(result, Ok(()));
332+
}
333+
334+
#[test]
335+
fn agreement_traits() {
336+
use crate::test;
337+
338+
let rng = rand::SystemRandom::new();
339+
340+
let ephemeral_private_key =
341+
agreement::EphemeralPrivateKey::generate_for_test(&agreement::ECDH_P256, &rng).unwrap();
342+
343+
test::compile_time_assert_send::<agreement::EphemeralPrivateKey>();
344+
test::compile_time_assert_sync::<agreement::EphemeralPrivateKey>();
345+
346+
assert_eq!(
347+
format!("{:?}", &ephemeral_private_key),
348+
"EphemeralPrivateKey { algorithm: Algorithm { curve: P256 } }"
349+
);
350+
}
351+
352+
#[test]
353+
fn agreement_agree_ephemeral() {
354+
let rng = rand::SystemRandom::new();
355+
356+
test::run(
357+
test_file!("data/agreement_tests.txt"),
358+
|section, test_case| {
359+
assert_eq!(section, "");
360+
361+
let curve_name = test_case.consume_string("Curve");
362+
let alg = alg_from_curve_name(&curve_name);
363+
let peer_public =
364+
agreement::UnparsedPublicKey::new(alg, test_case.consume_bytes("PeerQ"));
365+
366+
match test_case.consume_optional_string("Error") {
367+
None => {
368+
let my_private_bytes = test_case.consume_bytes("D");
369+
let my_private = {
370+
let rng = test::rand::FixedSliceRandom {
371+
bytes: &my_private_bytes,
372+
};
373+
agreement::EphemeralPrivateKey::generate_for_test(alg, &rng)?
374+
};
375+
let my_public = test_case.consume_bytes("MyQ");
376+
let output = test_case.consume_bytes("Output");
377+
378+
assert_eq!(my_private.algorithm(), alg);
379+
380+
let computed_public = my_private.compute_public_key().unwrap();
381+
assert_eq!(computed_public.as_ref(), &my_public[..]);
382+
383+
assert_eq!(my_private.algorithm(), alg);
384+
385+
let result = agreement::agree_ephemeral(
386+
my_private,
387+
&peer_public,
388+
(),
389+
|key_material| {
390+
assert_eq!(key_material, &output[..]);
391+
Ok(())
392+
},
393+
);
394+
assert_eq!(
395+
result,
396+
Ok(()),
397+
"Failed on private key: {:?}",
398+
test::to_hex(my_private_bytes)
399+
);
400+
}
401+
402+
Some(_) => {
403+
fn kdf_not_called(_: &[u8]) -> Result<(), ()> {
404+
panic!(
405+
"The KDF was called during ECDH when the peer's \
406+
public key is invalid."
407+
);
408+
}
409+
let dummy_private_key =
410+
agreement::EphemeralPrivateKey::generate(alg, &rng)?;
411+
assert!(agreement::agree_ephemeral(
412+
dummy_private_key,
413+
&peer_public,
414+
(),
415+
kdf_not_called
416+
)
417+
.is_err());
418+
}
419+
}
420+
421+
Ok(())
422+
},
423+
);
424+
}
425+
426+
fn h(s: &str) -> Vec<u8> {
427+
match test::from_hex(s) {
428+
Ok(v) => v,
429+
Err(msg) => {
430+
panic!("{msg} in {s}");
431+
}
432+
}
433+
}
434+
435+
fn alg_from_curve_name(curve_name: &str) -> &'static agreement::Algorithm {
436+
if curve_name == "P-256" {
437+
&agreement::ECDH_P256
438+
} else if curve_name == "P-384" {
439+
&agreement::ECDH_P384
440+
} else if curve_name == "P-521" {
441+
&agreement::ECDH_P521
442+
} else if curve_name == "X25519" {
443+
&agreement::X25519
444+
} else {
445+
panic!("Unsupported curve: {curve_name}");
446+
}
447+
}
448+
449+
fn x25519(private_key: &[u8], public_key: &[u8]) -> Vec<u8> {
450+
x25519_(private_key, public_key).unwrap()
451+
}
452+
453+
fn x25519_(private_key: &[u8], public_key: &[u8]) -> Result<Vec<u8>, Unspecified> {
454+
let rng = test::rand::FixedSliceRandom { bytes: private_key };
455+
let private_key =
456+
agreement::EphemeralPrivateKey::generate_for_test(&agreement::X25519, &rng)?;
457+
let public_key = agreement::UnparsedPublicKey::new(&agreement::X25519, public_key);
458+
agreement::agree_ephemeral(private_key, &public_key, Unspecified, |agreed_value| {
459+
Ok(Vec::from(agreed_value))
460+
})
461+
}
462+
}

0 commit comments

Comments
 (0)