ring/ec/suite_b/ecdsa/
signing.rs

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