ring/ec/suite_b/ecdsa/digest_scalar.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 AUTHOR DISCLAIMS ALL WARRANTIES
8// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR 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 crate::{digest, ec::suite_b::ops::*};
18
19/// Calculate the digest of `msg` using the digest algorithm `digest_alg`. Then
20/// convert the digest to a scalar in the range [0, n) as described in
21/// NIST's FIPS 186-4 Section 4.2. Note that this is one of the few cases where
22/// a `Scalar` is allowed to have the value zero.
23///
24/// NIST's FIPS 186-4 4.2 says "When the length of the output of the hash
25/// function is greater than N (i.e., the bit length of q), then the leftmost N
26/// bits of the hash function output block shall be used in any calculation
27/// using the hash function output during the generation or verification of a
28/// digital signature."
29///
30/// "Leftmost N bits" means "N most significant bits" because we interpret the
31/// digest as a bit-endian encoded integer.
32///
33/// The NSA guide instead vaguely suggests that we should convert the digest
34/// value to an integer and then reduce it mod `n`. However, real-world
35/// implementations (e.g. `digest_to_bn` in OpenSSL and `hashToInt` in Go) do
36/// what FIPS 186-4 says to do, not what the NSA guide suggests.
37///
38/// Why shifting the value right by at most one bit is sufficient: P-256's `n`
39/// has its 256th bit set; i.e. 2**255 < n < 2**256. Once we've truncated the
40/// digest to 256 bits and converted it to an integer, it will have a value
41/// less than 2**256. If the value is larger than `n` then shifting it one bit
42/// right will give a value less than 2**255, which is less than `n`. The
43/// analogous argument applies for P-384. However, it does *not* apply in
44/// general; for example, it doesn't apply to P-521.
45pub(super) fn digest_scalar(n: &Modulus<N>, msg: digest::Digest) -> Scalar {
46 digest_scalar_(n, msg.as_ref())
47}
48
49#[cfg(test)]
50pub(super) fn digest_bytes_scalar(n: &Modulus<N>, digest: &[u8]) -> Scalar {
51 digest_scalar_(n, digest)
52}
53
54// This is a separate function solely so that we can test specific digest
55// values like all-zero values and values larger than `n`.
56fn digest_scalar_(n: &Modulus<N>, digest: &[u8]) -> Scalar {
57 let len = n.bytes_len();
58 let digest = if digest.len() > len {
59 &digest[..len]
60 } else {
61 digest
62 };
63
64 scalar_parse_big_endian_partially_reduced_variable_consttime(n, untrusted::Input::from(digest))
65 .unwrap()
66}
67
68#[cfg(test)]
69mod tests {
70 use super::digest_bytes_scalar;
71 use crate::{cpu, digest, ec::suite_b::ops::*, limb, test};
72
73 #[test]
74 fn test() {
75 let cpu = cpu::features();
76 test::run(
77 test_file!("ecdsa_digest_scalar_tests.txt"),
78 |section, test_case| {
79 assert_eq!(section, "");
80
81 let curve_name = test_case.consume_string("Curve");
82 let digest_name = test_case.consume_string("Digest");
83 let input = test_case.consume_bytes("Input");
84 let output = test_case.consume_bytes("Output");
85
86 let (ops, digest_alg) = match (curve_name.as_str(), digest_name.as_str()) {
87 ("P-256", "SHA256") => (&p256::PUBLIC_SCALAR_OPS, &digest::SHA256),
88 ("P-256", "SHA384") => (&p256::PUBLIC_SCALAR_OPS, &digest::SHA384),
89 ("P-384", "SHA256") => (&p384::PUBLIC_SCALAR_OPS, &digest::SHA256),
90 ("P-384", "SHA384") => (&p384::PUBLIC_SCALAR_OPS, &digest::SHA384),
91 _ => {
92 panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name);
93 }
94 };
95 let n = &ops.scalar_ops.scalar_modulus(cpu);
96
97 assert_eq!(input.len(), digest_alg.output_len());
98 assert_eq!(output.len(), ops.scalar_ops.scalar_bytes_len());
99 assert_eq!(output.len(), n.bytes_len());
100
101 let expected = scalar_parse_big_endian_variable(
102 n,
103 limb::AllowZero::Yes,
104 untrusted::Input::from(&output),
105 )
106 .unwrap();
107
108 let actual = digest_bytes_scalar(n, &input);
109 assert_eq!(
110 ops.scalar_ops.leak_limbs(&actual),
111 ops.scalar_ops.leak_limbs(&expected)
112 );
113
114 Ok(())
115 },
116 );
117 }
118}