Skip to content

Commit 17c7ead

Browse files
committed
ec: implement P521 signature verification
This code almost entirely reuses the P384 code, and only implements the dedicated inversion routines on top of the code generated by generate_curves.py
1 parent a80d5cb commit 17c7ead

File tree

15 files changed

+2466
-6
lines changed

15 files changed

+2466
-6
lines changed

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ include = [
7373
"crypto/fipsmodule/ec/gfp.h",
7474
"crypto/fipsmodule/ec/gfp_p256.c",
7575
"crypto/fipsmodule/ec/gfp_p384.c",
76+
"crypto/fipsmodule/ec/gfp_p521.c",
7677
"crypto/fipsmodule/ec/p256.c",
7778
"crypto/fipsmodule/ec/p256-nistz-table.h",
7879
"crypto/fipsmodule/ec/p256-nistz.c",

build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ const RING_SRCS: &[(&[&str], &str)] = &[
4242
(&[], "crypto/fipsmodule/ec/ecp_nistz.c"),
4343
(&[], "crypto/fipsmodule/ec/gfp_p256.c"),
4444
(&[], "crypto/fipsmodule/ec/gfp_p384.c"),
45+
(&[], "crypto/fipsmodule/ec/gfp_p521.c"),
4546
(&[], "crypto/fipsmodule/ec/p256.c"),
4647
(&[], "crypto/limbs/limbs.c"),
4748
(&[], "crypto/mem.c"),
@@ -967,6 +968,14 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
967968
"p384_point_double",
968969
"p384_point_mul",
969970
"p384_scalar_mul_mont",
971+
"p521_elem_div_by_2",
972+
"p521_elem_mul_mont",
973+
"p521_elem_neg",
974+
"p521_elem_sub",
975+
"p521_point_add",
976+
"p521_point_double",
977+
"p521_point_mul",
978+
"p521_scalar_mul_mont",
970979
"openssl_poly1305_neon2_addmulmod",
971980
"openssl_poly1305_neon2_blocks",
972981
"sha256_block_data_order",

crypto/fipsmodule/ec/gfp_p521.c

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
2+
/* Copyright 2016-2023 Brian Smith.
3+
*
4+
* Permission to use, copy, modify, and/or distribute this software for any
5+
* purpose with or without fee is hereby granted, provided that the above
6+
* copyright notice and this permission notice appear in all copies.
7+
*
8+
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
9+
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10+
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
11+
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12+
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13+
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14+
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
15+
16+
#include "../../limbs/limbs.h"
17+
#include "../bn/internal.h"
18+
#include "../../internal.h"
19+
20+
#include "../../limbs/limbs.inl"
21+
22+
#define BITS 521
23+
24+
#define P521_LIMBS ((521 + LIMB_BITS - 1) / LIMB_BITS)
25+
26+
#define FE_LIMBS P521_LIMBS
27+
28+
typedef Limb Elem[FE_LIMBS];
29+
typedef Limb ScalarMont[FE_LIMBS];
30+
typedef Limb Scalar[FE_LIMBS];
31+
32+
static const Elem Q = {
33+
#if defined(OPENSSL_64_BIT)
34+
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
35+
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
36+
0x1ff
37+
#else
38+
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
39+
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
40+
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1ff
41+
#endif
42+
};
43+
44+
static const Elem N = {
45+
#if defined(OPENSSL_64_BIT)
46+
0xbb6fb71e91386409, 0x3bb5c9b8899c47ae, 0x7fcc0148f709a5d0, 0x51868783bf2f966b,
47+
0xfffffffffffffffa, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
48+
0x1ff
49+
#else
50+
0x91386409, 0xbb6fb71e, 0x899c47ae, 0x3bb5c9b8, 0xf709a5d0, 0x7fcc0148,
51+
0xbf2f966b, 0x51868783, 0xfffffffa, 0xffffffff, 0xffffffff, 0xffffffff,
52+
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1ff
53+
#endif
54+
};
55+
56+
static const Elem ONE = {
57+
#if defined(OPENSSL_64_BIT)
58+
0x80000000000000, 0, 0, 0, 0, 0, 0, 0, 0
59+
#else
60+
0x800000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
61+
#endif
62+
};
63+
64+
static const Elem Q_PLUS_1_SHR_1 = {
65+
#if defined(OPENSSL_64_BIT)
66+
0, 0, 0, 0, 0, 0, 0, 0, 0x100
67+
#else
68+
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x100
69+
#endif
70+
};
71+
72+
static const BN_ULONG Q_N0[] = {
73+
BN_MONT_CTX_N0(0, 1)
74+
};
75+
76+
static const BN_ULONG N_N0[] = {
77+
BN_MONT_CTX_N0(0x1d2f5ccd, 0x79a995c7)
78+
};
79+
80+
/* XXX: MSVC for x86 warns when it fails to inline these functions it should
81+
* probably inline. */
82+
#if defined(_MSC_VER) && !defined(__clang__) && defined(OPENSSL_X86)
83+
#define INLINE_IF_POSSIBLE __forceinline
84+
#else
85+
#define INLINE_IF_POSSIBLE inline
86+
#endif
87+
88+
/* Window values that are Ok for P384 (look at `ecp_nistz.h`): 2, 5, 6, 7 */
89+
/* Window values that are Ok for P521 (look at `ecp_nistz.h`): 4 */
90+
#define W_BITS 4
91+
92+
#include "ecp_nistz.inl"
93+

crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt

Lines changed: 668 additions & 0 deletions
Large diffs are not rendered by default.

mk/generate_curves.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ def generate_rs(g, out_dir):
348348
349349
#define BITS %(bits)d
350350
351-
#define P%(bits)d_LIMBS (%(bits)du / LIMB_BITS)
351+
#define P%(bits)d_LIMBS ((%(bits)d + LIMB_BITS - 1) / LIMB_BITS)
352352
353353
#define FE_LIMBS P%(bits)d_LIMBS
354354

src/ec.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ pub enum CurveID {
4141
P384,
4242
}
4343

44-
const ELEM_MAX_BITS: usize = 384;
44+
const ELEM_MAX_BITS: usize = 521;
4545
pub const ELEM_MAX_BYTES: usize = (ELEM_MAX_BITS + 7) / 8;
4646

4747
pub const SCALAR_MAX_BYTES: usize = ELEM_MAX_BYTES;

src/ec/suite_b/ecdsa/digest_scalar.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,12 +55,26 @@ pub(crate) fn digest_bytes_scalar(ops: &ScalarOps, digest: &[u8]) -> Scalar {
5555
// values like all-zero values and values larger than `n`.
5656
fn digest_scalar_(ops: &ScalarOps, digest: &[u8]) -> Scalar {
5757
let len = ops.scalar_bytes_len();
58-
let digest = if digest.len() > len {
58+
let mut digest = if digest.len() > len {
5959
&digest[..len]
6060
} else {
6161
digest
6262
};
6363

64+
let mut digest_shift = [0u8; MAX_LIMBS * crate::limb::LIMB_BYTES];
65+
let shift = (digest.len() * 8).saturating_sub(ops.common.order_bits());
66+
if shift > 0 {
67+
// If the digest is too long after byte trancation
68+
// shift right to get the proper number of bits
69+
// This should not happen in practice for the supported curve/digest combos
70+
debug_assert!(shift < 8);
71+
digest_shift[0] = digest[0] >> shift;
72+
for i in 1..len {
73+
digest_shift[i] = digest[i] >> shift | digest[i - 1] << (8 - shift);
74+
}
75+
digest = &digest_shift[..len];
76+
}
77+
6478
scalar_parse_big_endian_partially_reduced_variable_consttime(
6579
ops.common,
6680
untrusted::Input::from(digest),

src/ec/suite_b/ecdsa/verification.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,10 @@ enum AlgorithmID {
4545
ECDSA_P384_SHA256_ASN1,
4646
ECDSA_P384_SHA384_ASN1,
4747
ECDSA_P384_SHA384_FIXED,
48+
ECDSA_P521_SHA384_FIXED,
49+
ECDSA_P521_SHA512_FIXED,
50+
ECDSA_P521_SHA384_ASN1,
51+
ECDSA_P521_SHA512_ASN1,
4852
}
4953

5054
derive_debug_via_id!(EcdsaVerificationAlgorithm);
@@ -214,6 +218,30 @@ pub static ECDSA_P384_SHA384_FIXED: EcdsaVerificationAlgorithm = EcdsaVerificati
214218
id: AlgorithmID::ECDSA_P384_SHA384_FIXED,
215219
};
216220

221+
/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
222+
/// P-521 curve and SHA-384.
223+
///
224+
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
225+
/// documentation for more details.
226+
pub static ECDSA_P521_SHA384_FIXED: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
227+
ops: &p521::PUBLIC_SCALAR_OPS,
228+
digest_alg: &digest::SHA384,
229+
split_rs: split_rs_fixed,
230+
id: AlgorithmID::ECDSA_P521_SHA384_FIXED,
231+
};
232+
233+
/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
234+
/// P-521 curve and SHA-512.
235+
///
236+
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
237+
/// documentation for more details.
238+
pub static ECDSA_P521_SHA512_FIXED: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
239+
ops: &p521::PUBLIC_SCALAR_OPS,
240+
digest_alg: &digest::SHA512,
241+
split_rs: split_rs_fixed,
242+
id: AlgorithmID::ECDSA_P521_SHA512_FIXED,
243+
};
244+
217245
/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-256 curve
218246
/// and SHA-256.
219247
///
@@ -272,6 +300,30 @@ pub static ECDSA_P384_SHA384_ASN1: EcdsaVerificationAlgorithm = EcdsaVerificatio
272300
id: AlgorithmID::ECDSA_P384_SHA384_ASN1,
273301
};
274302

303+
/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-521 curve
304+
/// and SHA-384.
305+
///
306+
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
307+
/// documentation for more details.
308+
pub static ECDSA_P521_SHA384_ASN1: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
309+
ops: &p521::PUBLIC_SCALAR_OPS,
310+
digest_alg: &digest::SHA384,
311+
split_rs: split_rs_asn1,
312+
id: AlgorithmID::ECDSA_P521_SHA384_ASN1,
313+
};
314+
315+
/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-521 curve
316+
/// and SHA-512.
317+
///
318+
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
319+
/// documentation for more details.
320+
pub static ECDSA_P521_SHA512_ASN1: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
321+
ops: &p521::PUBLIC_SCALAR_OPS,
322+
digest_alg: &digest::SHA512,
323+
split_rs: split_rs_asn1,
324+
id: AlgorithmID::ECDSA_P521_SHA512_ASN1,
325+
};
326+
275327
#[cfg(test)]
276328
mod tests {
277329
extern crate alloc;
@@ -309,6 +361,7 @@ mod tests {
309361
let alg = match curve_name.as_str() {
310362
"P-256" => &ECDSA_P256_SHA256_FIXED,
311363
"P-384" => &ECDSA_P384_SHA384_FIXED,
364+
"P-521" => &ECDSA_P521_SHA512_FIXED,
312365
_ => {
313366
panic!("Unsupported curve: {}", curve_name);
314367
}

src/ec/suite_b/ops.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ impl Point {
5353
/// Operations and values needed by all curve operations.
5454
pub struct CommonOps {
5555
num_limbs: usize,
56+
order_bits: usize,
5657
q: Modulus,
5758
n: Elem<Unencoded>,
5859

@@ -70,7 +71,11 @@ impl CommonOps {
7071
// The length of a field element, which is the same as the length of a
7172
// scalar, in bytes.
7273
pub fn len(&self) -> usize {
73-
self.num_limbs * LIMB_BYTES
74+
(self.order_bits + 7) / 8
75+
}
76+
77+
pub fn order_bits(&self) -> usize {
78+
self.order_bits
7479
}
7580

7681
#[cfg(test)]
@@ -1014,6 +1019,15 @@ mod tests {
10141019
);
10151020
}
10161021

1022+
#[test]
1023+
fn p521_point_mul_base_test() {
1024+
point_mul_base_tests(
1025+
&p521::PRIVATE_KEY_OPS,
1026+
|s| p521::PRIVATE_KEY_OPS.point_mul_base(s),
1027+
test_file!("ops/p521_point_mul_base_tests.txt"),
1028+
);
1029+
}
1030+
10171031
pub(super) fn point_mul_base_tests(
10181032
ops: &PrivateKeyOps,
10191033
f: impl Fn(&Scalar) -> Point,
@@ -1215,3 +1229,4 @@ mod tests {
12151229
mod elem;
12161230
pub mod p256;
12171231
pub mod p384;
1232+
pub mod p521;

src/ec/suite_b/ops/elem.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,4 +128,4 @@ pub fn unary_op_from_binary_op_assign<M, E: Encoding>(
128128
unsafe { f(a.limbs.as_mut_ptr(), a.limbs.as_ptr(), a.limbs.as_ptr()) }
129129
}
130130

131-
pub const MAX_LIMBS: usize = (384 + (LIMB_BITS - 1)) / LIMB_BITS;
131+
pub const MAX_LIMBS: usize = (521 + (LIMB_BITS - 1)) / LIMB_BITS;

src/ec/suite_b/ops/p256.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use super::{
1919

2020
pub static COMMON_OPS: CommonOps = CommonOps {
2121
num_limbs: 256 / LIMB_BITS,
22+
order_bits: 256,
2223

2324
q: Modulus {
2425
p: limbs_from_hex("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"),

src/ec/suite_b/ops/p384.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use super::{
1919

2020
pub static COMMON_OPS: CommonOps = CommonOps {
2121
num_limbs: 384 / LIMB_BITS,
22+
order_bits: 384,
2223

2324
q: Modulus {
2425
p: limbs_from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff"),

0 commit comments

Comments
 (0)