@@ -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