Skip to content

Commit

Permalink
Initial ECDSA signing implementation.
Browse files Browse the repository at this point in the history
Nonce reuse hardening and more tests will be added later.
  • Loading branch information
briansmith committed May 31, 2018
1 parent 772306c commit e5a4fe9
Show file tree
Hide file tree
Showing 14 changed files with 722 additions and 39 deletions.
2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,8 @@ include = [
"tests/digest_tests.txt",
"tests/ecdsa_from_pkcs8_tests.txt",
"tests/ecdsa_tests.rs",
"tests/ecdsa_sign_asn1_tests.txt",
"tests/ecdsa_sign_fixed_tests.txt",
"tests/ecdsa_verify_asn1_tests.txt",
"tests/ecdsa_verify_fixed_tests.txt",
"tests/ed25519_from_pkcs8_tests.txt",
Expand Down
6 changes: 2 additions & 4 deletions src/ec/suite_b/ecdsa/digest_scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,8 @@ use untrusted;
/// right will give a value less than 2**255, which is less than `n`. The
/// analogous argument applies for P-384. However, it does *not* apply in
/// general; for example, it doesn't apply to P-521.
pub fn digest_scalar(ops: &ScalarOps, digest_alg: &'static digest::Algorithm,
msg: untrusted::Input) -> Scalar {
let digest = digest::digest(digest_alg, msg.as_slice_less_safe());
digest_scalar_(ops, digest.as_ref())
pub fn digest_scalar(ops: &ScalarOps, msg: &digest::Digest) -> Scalar {
digest_scalar_(ops, msg.as_ref())
}

// This is a separate function solely so that we can test specific digest
Expand Down
225 changes: 211 additions & 14 deletions src/ec/suite_b/ecdsa/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,61 @@

//! ECDSA Signatures using the P-256 and P-384 curves.
use arithmetic::montgomery::*;
use core;
use {ec, error, pkcs8, rand};
use {der, digest, ec, error, pkcs8, rand, signature, signature_impl};
use super::digest_scalar::digest_scalar;
use ec::suite_b::{ops::*, private_key};
use untrusted;
use private;

/// An ECDSA signing algorithm.
pub struct Algorithm {
curve: &'static ec::Curve,
private_scalar_ops: &'static PrivateScalarOps,
private_key_ops: &'static PrivateKeyOps,
digest_alg: &'static digest::Algorithm,
pkcs8_template: &'static pkcs8::Template,
id: AlgorithmID
format_rs:
for <'a> fn(ops: &'static ScalarOps, r: &Scalar, s: &Scalar,
out: &'a mut [u8]) -> &'a [u8],
id: AlgorithmID,
}

#[derive(PartialEq, Eq)]
#[derive(Debug, Eq, PartialEq)]
enum AlgorithmID {
ECDSA_P256_SHA256_FIXED_SIGNING,
ECDSA_P384_SHA384_FIXED_SIGNING,
ECDSA_P256_SHA256_ASN1_SIGNING,
ECDSA_P384_SHA384_ASN1_SIGNING,
}

derive_debug_from_field!(Algorithm, id);

impl PartialEq for Algorithm {
fn eq(&self, other: &Self) -> bool { self.id == other.id }
}

impl Eq for Algorithm {}

impl private::Private for Algorithm {}

#[cfg(feature = "use_heap")]
impl signature::SigningAlgorithm for Algorithm {
fn from_pkcs8(&'static self, input: untrusted::Input)
-> Result<signature::KeyPair, error::Unspecified> {
Key::from_pkcs8(self, input).map(signature::KeyPair::new)
}
}

/// An ECDSA key pair, used for signing.
#[doc(hidden)]
pub struct Key {
#[allow(dead_code)] // XXX: Temporary, since signing isn't implemented yet.
key_pair: ec::KeyPair,

#[allow(dead_code)] // XXX: Temporary, since signing isn't implemented yet.
d: Scalar<R>,
alg: &'static Algorithm,
}

derive_debug_from_field!(Key, alg);

impl<'a> Key {
/// Generates a new key pair and returns the key pair serialized as a
/// PKCS#8 document.
Expand Down Expand Up @@ -85,7 +105,7 @@ impl<'a> Key {
-> Result<Self, error::Unspecified> {
let key_pair = ec::suite_b::key_pair_from_pkcs8(alg.curve,
alg.pkcs8_template, input)?;
Ok(Self { key_pair, alg })
Ok(Self::new(alg, key_pair))
}

/// Constructs an ECDSA key pair directly from the big-endian-encoded
Expand All @@ -100,19 +120,187 @@ impl<'a> Key {
-> Result<Self, error::Unspecified> {
let key_pair = ec::suite_b::key_pair_from_bytes(
alg.curve, private_key, public_key)?;
Ok(Self { key_pair, alg })
Ok(Self::new(alg, key_pair))
}

fn new(alg: &'static Algorithm, key_pair: ec::KeyPair) -> Self {
let d = private_key::private_key_as_scalar(
alg.private_key_ops, &key_pair.private_key);
let d = alg.private_scalar_ops.scalar_ops.scalar_product(
&d, &alg.private_scalar_ops.oneRR_mod_n);

Self { d, alg }
}

/// Deprecated.
pub fn sign(&self, msg: untrusted::Input, rng: &rand::SecureRandom)
-> Result<signature::Signature, error::Unspecified> {
// NSA Suite B Implementer's Guide to ECDSA Section 3.4.1: ECDSA
// Signature Generation.

// NSA Guide Prerequisites:
//
// Prior to generating an ECDSA signature, the signatory shall
// obtain:
//
// 1. an authentic copy of the domain parameters,
// 2. a digital signature key pair (d,Q), either generated by a
// method from Appendix A.1, or obtained from a trusted third
// party,
// 3. assurance of the validity of the public key Q (see Appendix
// A.3), and
// 4. assurance that he/she/it actually possesses the associated
// private key d (see [SP800-89] Section 6).
//
// The domain parameters are hard-coded into the source code.
// `ECDSAKeyPair::generate_pkcs8()` can be used to meet the second
// requirement; otherwise, it is up to the user to ensure the key pair
// was obtained from a trusted private key. The constructors for
// `ECDSAKeyPair` ensure that #3 and #4 are met subject to the caveats
// in SP800-89 Section 6.

// Step 4 (out of order).
let h = digest::digest(self.alg.digest_alg, msg.as_slice_less_safe());
self.sign_(&h, rng)
}

/// Returns the signature of message digest `h` using a "random" nonce
/// generated by `rng`.
///
/// This is the interface that the CAVP ECDSA tests require, since they
/// only provide the message digest, not the message itself.
fn sign_(&self, h: &digest::Digest, rng: &rand::SecureRandom)
-> Result<signature::Signature, error::Unspecified> {
let ops = self.alg.private_scalar_ops;
let scalar_ops = ops.scalar_ops;
let cops = scalar_ops.common;
let private_key_ops = self.alg.private_key_ops;

for _ in 0..100 { // XXX: iteration conut?
// Step 1.
let k = private_key::random_scalar(self.alg.private_key_ops, rng)?;
let k_inv = scalar_ops.scalar_inv_to_mont(&k);

// Step 2.
let r = private_key_ops.point_mul_base(&k);

// Step 3.
let r = {
let (x, _) = private_key::affine_from_jacobian(private_key_ops, &r)?;
let x = cops.elem_unencoded(&x);
elem_reduced_to_scalar(cops, &x)
};
if cops.is_zero(&r) {
continue;
}

// Step 4 is done by the caller.

// Step 5.
let e = digest_scalar(scalar_ops, h);

// Step 6.
let s = {
let dr = scalar_ops.scalar_product(&self.d, &r);
let e_plus_dr = scalar_sum(cops, &e, &dr);
scalar_ops.scalar_product(&k_inv, &e_plus_dr)
};
if cops.is_zero(&s) {
continue;
}

// Step 7 with encoding.
let mut sig_bytes = [0; signature_impl::MAX_LEN];
let sig =
(self.alg.format_rs)(scalar_ops, &r, &s, &mut sig_bytes[..]);
return Ok(signature_impl::signature_from_bytes(sig))
}

Err(error::Unspecified)
}
}

#[cfg(feature = "use_heap")]
impl signature::KeyPairImpl for Key {
/// Returns the signature of the message `msg`.
fn sign(&self, rng: &rand::SecureRandom, msg: untrusted::Input)
-> Result<signature::Signature, error::Unspecified>
{
Key::sign(self, msg, rng)
}
}

fn format_rs_fixed<'a>(ops: &'static ScalarOps, r: &Scalar, s: &Scalar,
out: &'a mut [u8]) -> &'a [u8] {
let scalar_len = ops.scalar_bytes_len();
{
let (r_out, rest) = out.split_at_mut(scalar_len);
big_endian_from_limbs(&r.limbs[..ops.common.num_limbs], r_out);
let (s_out, _) = rest.split_at_mut(scalar_len);
big_endian_from_limbs(&s.limbs[..ops.common.num_limbs], s_out);
}
&out[..(2 * scalar_len)]
}

fn format_rs_asn1<'a>(ops: &'static ScalarOps, r: &Scalar, s: &Scalar,
out: &'a mut [u8]) -> &'a [u8] {
// This assumes `a` is not zero since neither `r` or `s` is allowed to be
// zero.
fn format_integer_tlv(ops: &ScalarOps, a: &Scalar, out: &mut [u8]) -> usize {
let mut fixed = [0u8; ec::SCALAR_MAX_BYTES + 1];
let fixed = &mut fixed[..(ops.scalar_bytes_len() + 1)];
big_endian_from_limbs(&a.limbs[..ops.common.num_limbs], &mut fixed[1..]);

// Since `a_fixed_out` is an extra byte long, it is guaranteed to start
// with a zero.
debug_assert_eq!(fixed[0], 0);

// There must be at least one non-zero byte since `a` isn't zero.
let first_index = fixed.iter().position(|b| *b != 0).unwrap();

// If the first byte has its high bit set, it needs to be prefixed with 0x00.
let first_index = if fixed[first_index] & 0x80 != 0 {
first_index - 1
} else {
first_index
};
let value = &fixed[first_index..];

out[0] = der::Tag::Integer as u8;

// Lengths less than 128 are encoded in one byte.
assert!(value.len() < 128);
out[1] = value.len() as u8;

out[2..][..value.len()].copy_from_slice(&value);

2 + value.len()
}

out[0] = der::Tag::Sequence as u8;
let r_tlv_len = format_integer_tlv(ops, r, &mut out[2..]);
let s_tlv_len = format_integer_tlv(ops, s, &mut out[2..][r_tlv_len..]);

// Lengths less than 128 are encoded in one byte.
let value_len = r_tlv_len + s_tlv_len;
assert!(value_len < 128);
out[1] = value_len as u8;

&out[..(2 + value_len)]
}

/// Signing of fixed-length (PKCS#11 style) ECDSA signatures using the
/// P-256 curve and SHA-256.
///
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
/// documentation for more details.
#[doc(hidden)]
pub static ECDSA_P256_SHA256_FIXED_SIGNING: Algorithm = Algorithm {
curve: &ec::suite_b::curve::P256,
private_scalar_ops: &p256::PRIVATE_SCALAR_OPS,
private_key_ops: &p256::PRIVATE_KEY_OPS,
digest_alg: &digest::SHA256,
pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
format_rs: format_rs_fixed,
id: AlgorithmID::ECDSA_P256_SHA256_FIXED_SIGNING,
};

Expand All @@ -121,10 +309,13 @@ pub static ECDSA_P256_SHA256_FIXED_SIGNING: Algorithm = Algorithm {
///
/// See "`ECDSA_*_FIXED` Details" in `ring::signature`'s module-level
/// documentation for more details.
#[doc(hidden)]
pub static ECDSA_P384_SHA384_FIXED_SIGNING: Algorithm = Algorithm {
curve: &ec::suite_b::curve::P384,
private_scalar_ops: &p384::PRIVATE_SCALAR_OPS,
private_key_ops: &p384::PRIVATE_KEY_OPS,
digest_alg: &digest::SHA384,
pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
format_rs: format_rs_fixed,
id: AlgorithmID::ECDSA_P384_SHA384_FIXED_SIGNING,
};

Expand All @@ -133,10 +324,13 @@ pub static ECDSA_P384_SHA384_FIXED_SIGNING: Algorithm = Algorithm {
///
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
/// documentation for more details.
#[doc(hidden)]
pub static ECDSA_P256_SHA256_ASN1_SIGNING: Algorithm = Algorithm {
curve: &ec::suite_b::curve::P256,
private_scalar_ops: &p256::PRIVATE_SCALAR_OPS,
private_key_ops: &p256::PRIVATE_KEY_OPS,
digest_alg: &digest::SHA256,
pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
format_rs: format_rs_asn1,
id: AlgorithmID::ECDSA_P256_SHA256_ASN1_SIGNING,
};

Expand All @@ -145,10 +339,13 @@ pub static ECDSA_P256_SHA256_ASN1_SIGNING: Algorithm = Algorithm {
///
/// See "`ECDSA_*_ASN1` Details" in `ring::signature`'s module-level
/// documentation for more details.
#[doc(hidden)]
pub static ECDSA_P384_SHA384_ASN1_SIGNING: Algorithm = Algorithm {
curve: &ec::suite_b::curve::P384,
private_scalar_ops: &p384::PRIVATE_SCALAR_OPS,
private_key_ops: &p384::PRIVATE_KEY_OPS,
digest_alg: &digest::SHA384,
pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
format_rs: format_rs_asn1,
id: AlgorithmID::ECDSA_P384_SHA384_ASN1_SIGNING,
};

Expand Down
14 changes: 9 additions & 5 deletions src/ec/suite_b/ecdsa/verification.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,11 +80,15 @@ impl signature::VerificationAlgorithm for Algorithm {
let s = scalar_parse_big_endian_variable(public_key_ops.common,
AllowZero::No, s)?;

// NSA Guide Step 2: "Use the selected hash function to compute H =
// Hash(M)."
// NSA Guide Step 3: "Convert the bit string H to an integer e as
// described in Appendix B.2."
let e = digest_scalar(scalar_ops, self.digest_alg, msg);
let e = {
// NSA Guide Step 2: "Use the selected hash function to compute H =
// Hash(M)."
let h = digest::digest(self.digest_alg, msg.as_slice_less_safe());

// NSA Guide Step 3: "Convert the bit string H to an integer e as
// described in Appendix B.2."
digest_scalar(scalar_ops, &h)
};

// NSA Guide Step 4: "Compute w = s**−1 mod n, using the routine in
// Appendix B.1."
Expand Down
Loading

0 comments on commit e5a4fe9

Please sign in to comment.