1// Copyright 2015-2016 Brian Smith.
2//
3// Permission to use, copy, modify, and/or distribute this software for any
4// purpose with or without fee is hereby granted, provided that the above
5// copyright notice and this permission notice appear in all copies.
6//
7// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
10// SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
12// OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
13// CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
14
15//! ECDSA Signatures using the P-256 and P-384 curves.
16
17#![allow(clippy::cast_possible_truncation)] // XXX
18
19use super::digest_scalar::digest_scalar;
20use crate::{
21 arithmetic::montgomery::*,
22 cpu, digest,
23 ec::{
24 self,
25 suite_b::{ops::*, private_key},
26 },
27 error,
28 io::der,
29 limb, pkcs8, rand, sealed, signature,
30};
31/// An ECDSA signing algorithm.
32pub struct EcdsaSigningAlgorithm {
33 curve: &'static ec::Curve,
34 private_scalar_ops: &'static PrivateScalarOps,
35 private_key_ops: &'static PrivateKeyOps,
36 digest_alg: &'static digest::Algorithm,
37 pkcs8_template: &'static pkcs8::Template,
38 format_rs: fn(ops: &'static ScalarOps, r: &Scalar, s: &Scalar, out: &mut [u8]) -> usize,
39 id: AlgorithmID,
40}
41
42#[derive(Debug, Eq, PartialEq)]
43enum AlgorithmID {
44 ECDSA_P256_SHA256_FIXED_SIGNING,
45 ECDSA_P384_SHA384_FIXED_SIGNING,
46 ECDSA_P256_SHA256_ASN1_SIGNING,
47 ECDSA_P384_SHA384_ASN1_SIGNING,
48}
49
50derive_debug_via_id!(EcdsaSigningAlgorithm);
51
52impl PartialEq for EcdsaSigningAlgorithm {
53 fn eq(&self, other: &Self) -> bool {
54 self.id == other.id
55 }
56}
57
58impl Eq for EcdsaSigningAlgorithm {}
59
60impl sealed::Sealed for EcdsaSigningAlgorithm {}
61
62/// An ECDSA key pair, used for signing.
63pub struct EcdsaKeyPair {
64 d: Scalar<R>,
65 nonce_key: NonceRandomKey,
66 alg: &'static EcdsaSigningAlgorithm,
67 public_key: PublicKey,
68}
69
70derive_debug_via_field!(EcdsaKeyPair, stringify!(EcdsaKeyPair), public_key);
71
72impl EcdsaKeyPair {
73 /// Generates a new key pair and returns the key pair serialized as a
74 /// PKCS#8 document.
75 ///
76 /// The PKCS#8 document will be a v1 `OneAsymmetricKey` with the public key
77 /// included in the `ECPrivateKey` structure, as described in
78 /// [RFC 5958 Section 2] and [RFC 5915]. The `ECPrivateKey` structure will
79 /// not have a `parameters` field so the generated key is compatible with
80 /// PKCS#11.
81 ///
82 /// [RFC 5915]: https://tools.ietf.org/html/rfc5915
83 /// [RFC 5958 Section 2]: https://tools.ietf.org/html/rfc5958#section-2
84 pub fn generate_pkcs8(
85 alg: &'static EcdsaSigningAlgorithm,
86 rng: &dyn rand::SecureRandom,
87 ) -> Result<pkcs8::Document, error::Unspecified> {
88 let private_key = ec::Seed::generate(alg.curve, rng, cpu::features())?;
89 let public_key = private_key.compute_public_key()?;
90 Ok(pkcs8::wrap_key(
91 alg.pkcs8_template,
92 private_key.bytes_less_safe(),
93 public_key.as_ref(),
94 ))
95 }
96
97 /// Constructs an ECDSA key pair by parsing an unencrypted PKCS#8 v1
98 /// id-ecPublicKey `ECPrivateKey` key.
99 ///
100 /// The input must be in PKCS#8 v1 format. It must contain the public key in
101 /// the `ECPrivateKey` structure; `from_pkcs8()` will verify that the public
102 /// key and the private key are consistent with each other. The algorithm
103 /// identifier must identify the curve by name; it must not use an
104 /// "explicit" encoding of the curve. The `parameters` field of the
105 /// `ECPrivateKey`, if present, must be the same named curve that is in the
106 /// algorithm identifier in the PKCS#8 header.
107 pub fn from_pkcs8(
108 alg: &'static EcdsaSigningAlgorithm,
109 pkcs8: &[u8],
110 rng: &dyn rand::SecureRandom,
111 ) -> Result<Self, error::KeyRejected> {
112 let key_pair = ec::suite_b::key_pair_from_pkcs8(
113 alg.curve,
114 alg.pkcs8_template,
115 untrusted::Input::from(pkcs8),
116 cpu::features(),
117 )?;
118 Self::new(alg, key_pair, rng)
119 }
120
121 /// Constructs an ECDSA key pair from the private key and public key bytes
122 ///
123 /// The private key must encoded as a big-endian fixed-length integer. For
124 /// example, a P-256 private key must be 32 bytes prefixed with leading
125 /// zeros as needed.
126 ///
127 /// The public key is encoding in uncompressed form using the
128 /// Octet-String-to-Elliptic-Curve-Point algorithm in
129 /// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
130 ///
131 /// This is intended for use by code that deserializes key pairs. It is
132 /// recommended to use `EcdsaKeyPair::from_pkcs8()` (with a PKCS#8-encoded
133 /// key) instead.
134 ///
135 /// [SEC 1: Elliptic Curve Cryptography, Version 2.0]:
136 /// http://www.secg.org/sec1-v2.pdf
137 pub fn from_private_key_and_public_key(
138 alg: &'static EcdsaSigningAlgorithm,
139 private_key: &[u8],
140 public_key: &[u8],
141 rng: &dyn rand::SecureRandom,
142 ) -> Result<Self, error::KeyRejected> {
143 let key_pair = ec::suite_b::key_pair_from_bytes(
144 alg.curve,
145 untrusted::Input::from(private_key),
146 untrusted::Input::from(public_key),
147 cpu::features(),
148 )?;
149 Self::new(alg, key_pair, rng)
150 }
151
152 fn new(
153 alg: &'static EcdsaSigningAlgorithm,
154 key_pair: ec::KeyPair,
155 rng: &dyn rand::SecureRandom,
156 ) -> Result<Self, error::KeyRejected> {
157 let (seed, public_key) = key_pair.split();
158 let d = private_key::private_key_as_scalar(alg.private_key_ops, &seed);
159 let d = alg.private_scalar_ops.to_mont(&d);
160
161 let nonce_key = NonceRandomKey::new(alg, &seed, rng)?;
162 Ok(Self {
163 d,
164 nonce_key,
165 alg,
166 public_key: PublicKey(public_key),
167 })
168 }
169
170 /// Returns the signature of the `message` using a random nonce generated by `rng`.
171 pub fn sign(
172 &self,
173 rng: &dyn rand::SecureRandom,
174 message: &[u8],
175 ) -> Result<signature::Signature, error::Unspecified> {
176 // Step 4 (out of order).
177 let h = digest::digest(self.alg.digest_alg, message);
178
179 // Incorporate `h` into the nonce to hedge against faulty RNGs. (This
180 // is not an approved random number generator that is mandated in
181 // the spec.)
182 let nonce_rng = NonceRandom {
183 key: &self.nonce_key,
184 message_digest: &h,
185 rng,
186 };
187
188 self.sign_digest(h, &nonce_rng)
189 }
190
191 #[cfg(test)]
192 fn sign_with_fixed_nonce_during_test(
193 &self,
194 rng: &dyn rand::SecureRandom,
195 message: &[u8],
196 ) -> Result<signature::Signature, error::Unspecified> {
197 // Step 4 (out of order).
198 let h = digest::digest(self.alg.digest_alg, message);
199
200 self.sign_digest(h, rng)
201 }
202
203 /// Returns the signature of message digest `h` using a "random" nonce
204 /// generated by `rng`.
205 fn sign_digest(
206 &self,
207 h: digest::Digest,
208 rng: &dyn rand::SecureRandom,
209 ) -> Result<signature::Signature, error::Unspecified> {
210 // NSA Suite B Implementer's Guide to ECDSA Section 3.4.1: ECDSA
211 // Signature Generation.
212
213 // NSA Guide Prerequisites:
214 //
215 // Prior to generating an ECDSA signature, the signatory shall
216 // obtain:
217 //
218 // 1. an authentic copy of the domain parameters,
219 // 2. a digital signature key pair (d,Q), either generated by a
220 // method from Appendix A.1, or obtained from a trusted third
221 // party,
222 // 3. assurance of the validity of the public key Q (see Appendix
223 // A.3), and
224 // 4. assurance that he/she/it actually possesses the associated
225 // private key d (see [SP800-89] Section 6).
226 //
227 // The domain parameters are hard-coded into the source code.
228 // `EcdsaKeyPair::generate_pkcs8()` can be used to meet the second
229 // requirement; otherwise, it is up to the user to ensure the key pair
230 // was obtained from a trusted private key. The constructors for
231 // `EcdsaKeyPair` ensure that #3 and #4 are met subject to the caveats
232 // in SP800-89 Section 6.
233
234 let ops = self.alg.private_scalar_ops;
235 let scalar_ops = ops.scalar_ops;
236 let cops = scalar_ops.common;
237 let private_key_ops = self.alg.private_key_ops;
238
239 for _ in 0..100 {
240 // XXX: iteration conut?
241 // Step 1.
242 let k = private_key::random_scalar(self.alg.private_key_ops, rng)?;
243 let k_inv = ops.scalar_inv_to_mont(&k);
244
245 // Step 2.
246 let r = private_key_ops.point_mul_base(&k);
247
248 // Step 3.
249 let r = {
250 let (x, _) = private_key::affine_from_jacobian(private_key_ops, &r)?;
251 let x = cops.elem_unencoded(&x);
252 elem_reduced_to_scalar(cops, &x)
253 };
254 if cops.is_zero(&r) {
255 continue;
256 }
257
258 // Step 4 is done by the caller.
259
260 // Step 5.
261 let e = digest_scalar(scalar_ops, h);
262
263 // Step 6.
264 let s = {
265 let dr = scalar_ops.scalar_product(&self.d, &r);
266 let e_plus_dr = scalar_sum(cops, &e, dr);
267 scalar_ops.scalar_product(&k_inv, &e_plus_dr)
268 };
269 if cops.is_zero(&s) {
270 continue;
271 }
272
273 // Step 7 with encoding.
274 return Ok(signature::Signature::new(|sig_bytes| {
275 (self.alg.format_rs)(scalar_ops, &r, &s, sig_bytes)
276 }));
277 }
278
279 Err(error::Unspecified)
280 }
281}
282
283/// Generates an ECDSA nonce in a way that attempts to protect against a faulty
284/// `SecureRandom`.
285struct NonceRandom<'a> {
286 key: &'a NonceRandomKey,
287 message_digest: &'a digest::Digest,
288 rng: &'a dyn rand::SecureRandom,
289}
290
291impl core::fmt::Debug for NonceRandom<'_> {
292 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
293 f.debug_struct(name:"NonceRandom").finish()
294 }
295}
296
297impl rand::sealed::SecureRandom for NonceRandom<'_> {
298 fn fill_impl(&self, dest: &mut [u8]) -> Result<(), error::Unspecified> {
299 // Use the same digest algorithm that will be used to digest the
300 // message. The digest algorithm's output is exactly the right size;
301 // this is checked below.
302 //
303 // XXX(perf): The single iteration will require two digest block
304 // operations because the amount of data digested is larger than one
305 // block.
306 let digest_alg = self.key.0.algorithm();
307 let mut ctx = digest::Context::new(digest_alg);
308
309 // Digest the randomized digest of the private key.
310 let key = self.key.0.as_ref();
311 ctx.update(key);
312
313 // The random value is digested between the key and the message so that
314 // the key and the message are not directly digested in the same digest
315 // block.
316 assert!(key.len() <= digest_alg.block_len() / 2);
317 {
318 let mut rand = [0u8; digest::MAX_BLOCK_LEN];
319 let rand = &mut rand[..digest_alg.block_len() - key.len()];
320 assert!(rand.len() >= dest.len());
321 self.rng.fill(rand)?;
322 ctx.update(rand);
323 }
324
325 ctx.update(self.message_digest.as_ref());
326
327 let nonce = ctx.finish();
328
329 // `copy_from_slice()` panics if the lengths differ, so we don't have
330 // to separately assert that the lengths are the same.
331 dest.copy_from_slice(nonce.as_ref());
332
333 Ok(())
334 }
335}
336
337impl<'a> sealed::Sealed for NonceRandom<'a> {}
338
339struct NonceRandomKey(digest::Digest);
340
341impl NonceRandomKey {
342 fn new(
343 alg: &EcdsaSigningAlgorithm,
344 seed: &ec::Seed,
345 rng: &dyn rand::SecureRandom,
346 ) -> Result<Self, error::KeyRejected> {
347 let mut rand: [u8; 64] = [0; digest::MAX_OUTPUT_LEN];
348 let rand: &mut [u8] = &mut rand[0..alg.curve.elem_scalar_seed_len];
349
350 // XXX: `KeyRejected` isn't the right way to model failure of the RNG,
351 // but to fix that we'd need to break the API by changing the result type.
352 // TODO: Fix the API in the next breaking release.
353 rng.fill(rand)
354 .map_err(|error::Unspecified| error::KeyRejected::rng_failed())?;
355
356 let mut ctx: Context = digest::Context::new(algorithm:alg.digest_alg);
357 ctx.update(data:rand);
358 ctx.update(data:seed.bytes_less_safe());
359 Ok(Self(ctx.finish()))
360 }
361}
362
363impl signature::KeyPair for EcdsaKeyPair {
364 type PublicKey = PublicKey;
365
366 fn public_key(&self) -> &Self::PublicKey {
367 &self.public_key
368 }
369}
370
371#[derive(Clone, Copy)]
372pub struct PublicKey(ec::PublicKey);
373
374derive_debug_self_as_ref_hex_bytes!(PublicKey);
375
376impl AsRef<[u8]> for PublicKey {
377 fn as_ref(&self) -> &[u8] {
378 self.0.as_ref()
379 }
380}
381
382fn format_rs_fixed(ops: &'static ScalarOps, r: &Scalar, s: &Scalar, out: &mut [u8]) -> usize {
383 let scalar_len: usize = ops.scalar_bytes_len();
384
385 let (r_out: &mut [u8], rest: &mut [u8]) = out.split_at_mut(mid:scalar_len);
386 limb::big_endian_from_limbs(ops.leak_limbs(r), r_out);
387
388 let (s_out: &mut [u8], _) = rest.split_at_mut(mid:scalar_len);
389 limb::big_endian_from_limbs(ops.leak_limbs(s), s_out);
390
391 2 * scalar_len
392}
393
394fn format_rs_asn1(ops: &'static ScalarOps, r: &Scalar, s: &Scalar, out: &mut [u8]) -> usize {
395 // This assumes `a` is not zero since neither `r` or `s` is allowed to be
396 // zero.
397 fn format_integer_tlv(ops: &ScalarOps, a: &Scalar, out: &mut [u8]) -> usize {
398 let mut fixed = [0u8; ec::SCALAR_MAX_BYTES + 1];
399 let fixed = &mut fixed[..(ops.scalar_bytes_len() + 1)];
400 limb::big_endian_from_limbs(ops.leak_limbs(a), &mut fixed[1..]);
401
402 // Since `a_fixed_out` is an extra byte long, it is guaranteed to start
403 // with a zero.
404 debug_assert_eq!(fixed[0], 0);
405
406 // There must be at least one non-zero byte since `a` isn't zero.
407 let first_index = fixed.iter().position(|b| *b != 0).unwrap();
408
409 // If the first byte has its high bit set, it needs to be prefixed with 0x00.
410 let first_index = if fixed[first_index] & 0x80 != 0 {
411 first_index - 1
412 } else {
413 first_index
414 };
415 let value = &fixed[first_index..];
416
417 out[0] = der::Tag::Integer as u8;
418
419 // Lengths less than 128 are encoded in one byte.
420 assert!(value.len() < 128);
421 out[1] = value.len() as u8;
422
423 out[2..][..value.len()].copy_from_slice(value);
424
425 2 + value.len()
426 }
427
428 out[0] = der::Tag::Sequence as u8;
429 let r_tlv_len = format_integer_tlv(ops, r, &mut out[2..]);
430 let s_tlv_len = format_integer_tlv(ops, s, &mut out[2..][r_tlv_len..]);
431
432 // Lengths less than 128 are encoded in one byte.
433 let value_len = r_tlv_len + s_tlv_len;
434 assert!(value_len < 128);
435 out[1] = value_len as u8;
436
437 2 + value_len
438}
439
440/// Signing of fixed-length (PKCS#11 style) ECDSA signatures using the
441/// P-256 curve and SHA-256.
442///
443/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
444/// documentation for more details.
445pub static ECDSA_P256_SHA256_FIXED_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
446 curve: &ec::suite_b::curve::P256,
447 private_scalar_ops: &p256::PRIVATE_SCALAR_OPS,
448 private_key_ops: &p256::PRIVATE_KEY_OPS,
449 digest_alg: &digest::SHA256,
450 pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
451 format_rs: format_rs_fixed,
452 id: AlgorithmID::ECDSA_P256_SHA256_FIXED_SIGNING,
453};
454
455/// Signing of fixed-length (PKCS#11 style) ECDSA signatures using the
456/// P-384 curve and SHA-384.
457///
458/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
459/// documentation for more details.
460pub static ECDSA_P384_SHA384_FIXED_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
461 curve: &ec::suite_b::curve::P384,
462 private_scalar_ops: &p384::PRIVATE_SCALAR_OPS,
463 private_key_ops: &p384::PRIVATE_KEY_OPS,
464 digest_alg: &digest::SHA384,
465 pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
466 format_rs: format_rs_fixed,
467 id: AlgorithmID::ECDSA_P384_SHA384_FIXED_SIGNING,
468};
469
470/// Signing of ASN.1 DER-encoded ECDSA signatures using the P-256 curve and
471/// SHA-256.
472///
473/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
474/// documentation for more details.
475pub static ECDSA_P256_SHA256_ASN1_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
476 curve: &ec::suite_b::curve::P256,
477 private_scalar_ops: &p256::PRIVATE_SCALAR_OPS,
478 private_key_ops: &p256::PRIVATE_KEY_OPS,
479 digest_alg: &digest::SHA256,
480 pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
481 format_rs: format_rs_asn1,
482 id: AlgorithmID::ECDSA_P256_SHA256_ASN1_SIGNING,
483};
484
485/// Signing of ASN.1 DER-encoded ECDSA signatures using the P-384 curve and
486/// SHA-384.
487///
488/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
489/// documentation for more details.
490pub static ECDSA_P384_SHA384_ASN1_SIGNING: EcdsaSigningAlgorithm = EcdsaSigningAlgorithm {
491 curve: &ec::suite_b::curve::P384,
492 private_scalar_ops: &p384::PRIVATE_SCALAR_OPS,
493 private_key_ops: &p384::PRIVATE_KEY_OPS,
494 digest_alg: &digest::SHA384,
495 pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
496 format_rs: format_rs_asn1,
497 id: AlgorithmID::ECDSA_P384_SHA384_ASN1_SIGNING,
498};
499
500static EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
501 bytes: include_bytes!("ecPublicKey_p256_pkcs8_v1_template.der"),
502 alg_id_range: core::ops::Range { start: 8, end: 27 },
503 curve_id_index: 9,
504 private_key_index: 0x24,
505};
506
507static EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
508 bytes: include_bytes!("ecPublicKey_p384_pkcs8_v1_template.der"),
509 alg_id_range: core::ops::Range { start: 8, end: 24 },
510 curve_id_index: 9,
511 private_key_index: 0x23,
512};
513
514#[cfg(test)]
515mod tests {
516 use crate::{rand, signature, test};
517
518 #[test]
519 fn signature_ecdsa_sign_fixed_test() {
520 let rng = rand::SystemRandom::new();
521
522 test::run(
523 test_file!("ecdsa_sign_fixed_tests.txt"),
524 |section, test_case| {
525 assert_eq!(section, "");
526
527 let curve_name = test_case.consume_string("Curve");
528 let digest_name = test_case.consume_string("Digest");
529 let msg = test_case.consume_bytes("Msg");
530 let d = test_case.consume_bytes("d");
531 let q = test_case.consume_bytes("Q");
532 let k = test_case.consume_bytes("k");
533
534 let expected_result = test_case.consume_bytes("Sig");
535
536 let alg = match (curve_name.as_str(), digest_name.as_str()) {
537 ("P-256", "SHA256") => &signature::ECDSA_P256_SHA256_FIXED_SIGNING,
538 ("P-384", "SHA384") => &signature::ECDSA_P384_SHA384_FIXED_SIGNING,
539 _ => {
540 panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name);
541 }
542 };
543
544 let private_key =
545 signature::EcdsaKeyPair::from_private_key_and_public_key(alg, &d, &q, &rng)
546 .unwrap();
547 let rng = test::rand::FixedSliceRandom { bytes: &k };
548
549 let actual_result = private_key
550 .sign_with_fixed_nonce_during_test(&rng, &msg)
551 .unwrap();
552
553 assert_eq!(actual_result.as_ref(), &expected_result[..]);
554
555 Ok(())
556 },
557 );
558 }
559
560 #[test]
561 fn signature_ecdsa_sign_asn1_test() {
562 let rng = rand::SystemRandom::new();
563
564 test::run(
565 test_file!("ecdsa_sign_asn1_tests.txt"),
566 |section, test_case| {
567 assert_eq!(section, "");
568
569 let curve_name = test_case.consume_string("Curve");
570 let digest_name = test_case.consume_string("Digest");
571 let msg = test_case.consume_bytes("Msg");
572 let d = test_case.consume_bytes("d");
573 let q = test_case.consume_bytes("Q");
574 let k = test_case.consume_bytes("k");
575
576 let expected_result = test_case.consume_bytes("Sig");
577
578 let alg = match (curve_name.as_str(), digest_name.as_str()) {
579 ("P-256", "SHA256") => &signature::ECDSA_P256_SHA256_ASN1_SIGNING,
580 ("P-384", "SHA384") => &signature::ECDSA_P384_SHA384_ASN1_SIGNING,
581 _ => {
582 panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name);
583 }
584 };
585
586 let private_key =
587 signature::EcdsaKeyPair::from_private_key_and_public_key(alg, &d, &q, &rng)
588 .unwrap();
589 let rng = test::rand::FixedSliceRandom { bytes: &k };
590
591 let actual_result = private_key
592 .sign_with_fixed_nonce_during_test(&rng, &msg)
593 .unwrap();
594
595 assert_eq!(actual_result.as_ref(), &expected_result[..]);
596
597 Ok(())
598 },
599 );
600 }
601}
602