Skip to content

Commit

Permalink
ec: implement P521 signature verification
Browse files Browse the repository at this point in the history
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
  • Loading branch information
vkrasnov committed Jan 24, 2024
1 parent e05cbe6 commit 7f35cec
Show file tree
Hide file tree
Showing 15 changed files with 2,466 additions and 6 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ include = [
"crypto/fipsmodule/ec/gfp.h",
"crypto/fipsmodule/ec/gfp_p256.c",
"crypto/fipsmodule/ec/gfp_p384.c",
"crypto/fipsmodule/ec/gfp_p521.c",
"crypto/fipsmodule/ec/p256.c",
"crypto/fipsmodule/ec/p256-nistz-table.h",
"crypto/fipsmodule/ec/p256-nistz.c",
Expand Down
9 changes: 9 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ const RING_SRCS: &[(&[&str], &str)] = &[
(&[], "crypto/fipsmodule/ec/ecp_nistz.c"),
(&[], "crypto/fipsmodule/ec/gfp_p256.c"),
(&[], "crypto/fipsmodule/ec/gfp_p384.c"),
(&[], "crypto/fipsmodule/ec/gfp_p521.c"),
(&[], "crypto/fipsmodule/ec/p256.c"),
(&[], "crypto/limbs/limbs.c"),
(&[], "crypto/mem.c"),
Expand Down Expand Up @@ -967,6 +968,14 @@ fn prefix_all_symbols(pp: char, prefix_prefix: &str, prefix: &str) -> String {
"p384_point_double",
"p384_point_mul",
"p384_scalar_mul_mont",
"p521_elem_div_by_2",
"p521_elem_mul_mont",
"p521_elem_neg",
"p521_elem_sub",
"p521_point_add",
"p521_point_double",
"p521_point_mul",
"p521_scalar_mul_mont",
"openssl_poly1305_neon2_addmulmod",
"openssl_poly1305_neon2_blocks",
"sha256_block_data_order",
Expand Down
93 changes: 93 additions & 0 deletions crypto/fipsmodule/ec/gfp_p521.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@

/* Copyright 2016-2023 Brian Smith.
*
* Permission to use, copy, modify, and/or distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHORS DISCLAIM ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY
* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */

#include "../../limbs/limbs.h"
#include "../bn/internal.h"
#include "../../internal.h"

#include "../../limbs/limbs.inl"

#define BITS 521

#define P521_LIMBS ((521 + LIMB_BITS - 1) / LIMB_BITS)

#define FE_LIMBS P521_LIMBS

typedef Limb Elem[FE_LIMBS];
typedef Limb ScalarMont[FE_LIMBS];
typedef Limb Scalar[FE_LIMBS];

static const Elem Q = {
#if defined(OPENSSL_64_BIT)
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
0x1ff
#else
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1ff
#endif
};

static const Elem N = {
#if defined(OPENSSL_64_BIT)
0xbb6fb71e91386409, 0x3bb5c9b8899c47ae, 0x7fcc0148f709a5d0, 0x51868783bf2f966b,
0xfffffffffffffffa, 0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
0x1ff
#else
0x91386409, 0xbb6fb71e, 0x899c47ae, 0x3bb5c9b8, 0xf709a5d0, 0x7fcc0148,
0xbf2f966b, 0x51868783, 0xfffffffa, 0xffffffff, 0xffffffff, 0xffffffff,
0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x1ff
#endif
};

static const Elem ONE = {
#if defined(OPENSSL_64_BIT)
0x80000000000000, 0, 0, 0, 0, 0, 0, 0, 0
#else
0x800000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
#endif
};

static const Elem Q_PLUS_1_SHR_1 = {
#if defined(OPENSSL_64_BIT)
0, 0, 0, 0, 0, 0, 0, 0, 0x100
#else
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x100
#endif
};

static const BN_ULONG Q_N0[] = {
BN_MONT_CTX_N0(0, 1)
};

static const BN_ULONG N_N0[] = {
BN_MONT_CTX_N0(0x1d2f5ccd, 0x79a995c7)
};

/* XXX: MSVC for x86 warns when it fails to inline these functions it should
* probably inline. */
#if defined(_MSC_VER) && !defined(__clang__) && defined(OPENSSL_X86)
#define INLINE_IF_POSSIBLE __forceinline
#else
#define INLINE_IF_POSSIBLE inline
#endif

/* Window values that are Ok for P384 (look at `ecp_nistz.h`): 2, 5, 6, 7 */
/* Window values that are Ok for P521 (look at `ecp_nistz.h`): 4 */
#define W_BITS 4

#include "ecp_nistz.inl"

668 changes: 668 additions & 0 deletions crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion mk/generate_curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,7 @@ def generate_rs(g, out_dir):
#define BITS %(bits)d
#define P%(bits)d_LIMBS (%(bits)du / LIMB_BITS)
#define P%(bits)d_LIMBS ((%(bits)d + LIMB_BITS - 1) / LIMB_BITS)
#define FE_LIMBS P%(bits)d_LIMBS
Expand Down
2 changes: 1 addition & 1 deletion src/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub enum CurveID {
P384,
}

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

pub const SCALAR_MAX_BYTES: usize = ELEM_MAX_BYTES;
Expand Down
16 changes: 15 additions & 1 deletion src/ec/suite_b/ecdsa/digest_scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,26 @@ pub(crate) fn digest_bytes_scalar(ops: &ScalarOps, digest: &[u8]) -> Scalar {
// values like all-zero values and values larger than `n`.
fn digest_scalar_(ops: &ScalarOps, digest: &[u8]) -> Scalar {
let len = ops.scalar_bytes_len();
let digest = if digest.len() > len {
let mut digest = if digest.len() > len {
&digest[..len]
} else {
digest
};

let mut digest_shift = [0u8; MAX_LIMBS * crate::limb::LIMB_BYTES];
let shift = (digest.len() * 8).saturating_sub(ops.common.order_bits());
if shift > 0 {
// If the digest is too long after byte trancation
// shift right to get the proper number of bits
// This should not happen in practice for the supported curve/digest combos
debug_assert!(shift < 8);
digest_shift[0] = digest[0] >> shift;
for i in 1..len {
digest_shift[i] = digest[i] >> shift | digest[i - 1] << (8 - shift);
}
digest = &digest_shift[..len];
}

scalar_parse_big_endian_partially_reduced_variable_consttime(
ops.common,
untrusted::Input::from(digest),
Expand Down
53 changes: 53 additions & 0 deletions src/ec/suite_b/ecdsa/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ enum AlgorithmID {
ECDSA_P384_SHA256_ASN1,
ECDSA_P384_SHA384_ASN1,
ECDSA_P384_SHA384_FIXED,
ECDSA_P521_SHA384_FIXED,
ECDSA_P521_SHA512_FIXED,
ECDSA_P521_SHA384_ASN1,
ECDSA_P521_SHA512_ASN1,
}

derive_debug_via_id!(EcdsaVerificationAlgorithm);
Expand Down Expand Up @@ -214,6 +218,30 @@ pub static ECDSA_P384_SHA384_FIXED: EcdsaVerificationAlgorithm = EcdsaVerificati
id: AlgorithmID::ECDSA_P384_SHA384_FIXED,
};

/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-521 curve and SHA-384.
///
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P521_SHA384_FIXED: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
ops: &p521::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_fixed,
id: AlgorithmID::ECDSA_P521_SHA384_FIXED,
};

/// Verification of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-521 curve and SHA-512.
///
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P521_SHA512_FIXED: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
ops: &p521::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA512,
split_rs: split_rs_fixed,
id: AlgorithmID::ECDSA_P521_SHA512_FIXED,
};

/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-256 curve
/// and SHA-256.
///
Expand Down Expand Up @@ -272,6 +300,30 @@ pub static ECDSA_P384_SHA384_ASN1: EcdsaVerificationAlgorithm = EcdsaVerificatio
id: AlgorithmID::ECDSA_P384_SHA384_ASN1,
};

/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-521 curve
/// and SHA-384.
///
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P521_SHA384_ASN1: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
ops: &p521::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_asn1,
id: AlgorithmID::ECDSA_P521_SHA384_ASN1,
};

/// Verification of ASN.1 DER-encoded ECDSA signatures using the P-521 curve
/// and SHA-512.
///
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
/// documentation for more details.
pub static ECDSA_P521_SHA512_ASN1: EcdsaVerificationAlgorithm = EcdsaVerificationAlgorithm {
ops: &p521::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA512,
split_rs: split_rs_asn1,
id: AlgorithmID::ECDSA_P521_SHA512_ASN1,
};

#[cfg(test)]
mod tests {
extern crate alloc;
Expand Down Expand Up @@ -309,6 +361,7 @@ mod tests {
let alg = match curve_name.as_str() {
"P-256" => &ECDSA_P256_SHA256_FIXED,
"P-384" => &ECDSA_P384_SHA384_FIXED,
"P-521" => &ECDSA_P521_SHA512_FIXED,
_ => {
panic!("Unsupported curve: {}", curve_name);
}
Expand Down
17 changes: 16 additions & 1 deletion src/ec/suite_b/ops.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ impl Point {
/// Operations and values needed by all curve operations.
pub struct CommonOps {
num_limbs: usize,
order_bits: usize,
q: Modulus,
n: Elem<Unencoded>,

Expand All @@ -70,7 +71,11 @@ impl CommonOps {
// The length of a field element, which is the same as the length of a
// scalar, in bytes.
pub fn len(&self) -> usize {
self.num_limbs * LIMB_BYTES
(self.order_bits + 7) / 8
}

pub fn order_bits(&self) -> usize {
self.order_bits
}

#[cfg(test)]
Expand Down Expand Up @@ -1014,6 +1019,15 @@ mod tests {
);
}

#[test]
fn p521_point_mul_base_test() {
point_mul_base_tests(
&p521::PRIVATE_KEY_OPS,
|s| p521::PRIVATE_KEY_OPS.point_mul_base(s),
test_file!("ops/p521_point_mul_base_tests.txt"),
);
}

pub(super) fn point_mul_base_tests(
ops: &PrivateKeyOps,
f: impl Fn(&Scalar) -> Point,
Expand Down Expand Up @@ -1216,3 +1230,4 @@ mod tests {
mod elem;
pub mod p256;
pub mod p384;
pub mod p521;
2 changes: 1 addition & 1 deletion src/ec/suite_b/ops/elem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,4 @@ pub fn unary_op_from_binary_op_assign<M, E: Encoding>(
unsafe { f(a.limbs.as_mut_ptr(), a.limbs.as_ptr(), a.limbs.as_ptr()) }
}

pub const MAX_LIMBS: usize = (384 + (LIMB_BITS - 1)) / LIMB_BITS;
pub const MAX_LIMBS: usize = (521 + (LIMB_BITS - 1)) / LIMB_BITS;
1 change: 1 addition & 0 deletions src/ec/suite_b/ops/p256.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use super::{

pub static COMMON_OPS: CommonOps = CommonOps {
num_limbs: 256 / LIMB_BITS,
order_bits: 256,

q: Modulus {
p: limbs_from_hex("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"),
Expand Down
1 change: 1 addition & 0 deletions src/ec/suite_b/ops/p384.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ use super::{

pub static COMMON_OPS: CommonOps = CommonOps {
num_limbs: 384 / LIMB_BITS,
order_bits: 384,

q: Modulus {
p: limbs_from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff"),
Expand Down
Loading

0 comments on commit 7f35cec

Please sign in to comment.