use arithmetic::montgomery::*;
use core;
use {der, digest, ec, error, pkcs8, private, rand, signature};
use super::verify_jacobian_point_is_on_the_curve;
use super::ops::*;
use super::public_key::*;
use untrusted;
pub struct ECDSASigningAlgorithm {
curve: &'static ec::Curve,
pkcs8_template: &'static pkcs8::Template,
id: ECDSASigningAlgorithmID
}
#[allow(non_camel_case_types)]
#[derive(PartialEq, Eq)]
enum ECDSASigningAlgorithmID {
ECDSA_P256_SHA256_FIXED_SIGNING,
ECDSA_P384_SHA384_FIXED_SIGNING,
ECDSA_P256_SHA256_ASN1_SIGNING,
ECDSA_P384_SHA384_ASN1_SIGNING,
}
impl PartialEq for ECDSASigningAlgorithm {
fn eq(&self, other: &Self) -> bool { self.id == other.id }
}
impl Eq for ECDSASigningAlgorithm {}
pub struct ECDSAVerificationAlgorithm {
ops: &'static PublicScalarOps,
digest_alg: &'static digest::Algorithm,
split_rs:
for<'a> fn(ops: &'static ScalarOps, input: &mut untrusted::Reader<'a>)
-> Result<(untrusted::Input<'a>, untrusted::Input<'a>),
error::Unspecified>,
id: ECDSAVerificationAlgorithmID,
}
#[allow(non_camel_case_types)]
enum ECDSAVerificationAlgorithmID {
ECDSA_P256_SHA256_ASN1,
ECDSA_P256_SHA256_FIXED,
ECDSA_P256_SHA384_ASN1,
ECDSA_P384_SHA256_ASN1,
ECDSA_P384_SHA384_ASN1,
ECDSA_P384_SHA384_FIXED,
}
impl core::fmt::Debug for ECDSAVerificationAlgorithm {
fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> {
use self::ECDSAVerificationAlgorithmID::*;
write!(f, "ring::signature::{}", match self.id {
ECDSA_P256_SHA256_ASN1 => "ECDSA_P256_SHA256_ASN1",
ECDSA_P256_SHA256_FIXED => "ECDSA_P256_SHA256_FIXED",
ECDSA_P256_SHA384_ASN1 => "ECDSA_P256_SHA384_ASN1",
ECDSA_P384_SHA256_ASN1 => "ECDSA_P384_SHA256_ASN1",
ECDSA_P384_SHA384_ASN1 => "ECDSA_P384_SHA384_ASN1",
ECDSA_P384_SHA384_FIXED => "ECDSA_P384_SHA384_FIXED",
})
}
}
impl signature::VerificationAlgorithm for ECDSAVerificationAlgorithm {
fn verify(&self, public_key: untrusted::Input, msg: untrusted::Input,
signature: untrusted::Input) -> Result<(), error::Unspecified> {
let public_key_ops = self.ops.public_key_ops;
let scalar_ops = self.ops.scalar_ops;
let peer_pub_key = parse_uncompressed_point(public_key_ops, public_key)?;
let (r, s) = signature.read_all(
error::Unspecified, |input| (self.split_rs)(scalar_ops, input))?;
let r = scalar_parse_big_endian_variable(public_key_ops.common,
AllowZero::No, r)?;
let s = scalar_parse_big_endian_variable(public_key_ops.common,
AllowZero::No, s)?;
let e = digest_scalar(scalar_ops, self.digest_alg, msg);
let w = scalar_ops.scalar_inv_to_mont(&s);
let u1 = scalar_ops.scalar_product(&e, &w);
let u2 = scalar_ops.scalar_product(&r, &w);
let product =
twin_mul(self.ops.private_key_ops, &u1, &u2, &peer_pub_key);
let z2 = verify_jacobian_point_is_on_the_curve(public_key_ops.common,
&product)?;
let x = public_key_ops.common.point_x(&product);
fn sig_r_equals_x(ops: &PublicScalarOps, r: &Elem<Unencoded>,
x: &Elem<R>, z2: &Elem<R>) -> bool {
let cops = ops.public_key_ops.common;
let r_jacobian = cops.elem_product(z2, r);
let x = cops.elem_unencoded(x);
ops.elem_equals(&r_jacobian, &x)
}
let r = self.ops.scalar_as_elem(&r);
if sig_r_equals_x(self.ops, &r, &x, &z2) {
return Ok(());
}
if self.ops.elem_less_than(&r, &self.ops.q_minus_n) {
let r_plus_n =
self.ops.elem_sum(&r, &public_key_ops.common.n);
if sig_r_equals_x(self.ops, &r_plus_n, &x, &z2) {
return Ok(());
}
}
Err(error::Unspecified)
}
}
impl private::Private for ECDSAVerificationAlgorithm {}
#[doc(hidden)]
pub struct ECDSAKeyPair {
#[allow(dead_code)] key_pair: ec::KeyPair,
#[allow(dead_code)] alg: &'static ECDSASigningAlgorithm,
}
impl<'a> ECDSAKeyPair {
pub fn generate_pkcs8(alg: &'static ECDSASigningAlgorithm,
rng: &rand::SecureRandom)
-> Result<pkcs8::PKCS8Document, error::Unspecified> {
let private_key = ec::PrivateKey::generate(alg.curve, rng)?;
let mut public_key_bytes = [0; ec::PUBLIC_KEY_MAX_LEN];
let public_key_bytes = &mut public_key_bytes[..alg.curve.public_key_len];
(alg.curve.public_from_private)(public_key_bytes, &private_key)?;
Ok(pkcs8::wrap_key(&alg.pkcs8_template, private_key.bytes(alg.curve),
public_key_bytes))
}
pub fn from_pkcs8(alg: &'static ECDSASigningAlgorithm,
input: untrusted::Input)
-> Result<ECDSAKeyPair, error::Unspecified> {
let key_pair = ec::suite_b::key_pair_from_pkcs8(alg.curve,
alg.pkcs8_template, input)?;
Ok(ECDSAKeyPair { key_pair, alg })
}
pub fn from_private_key_and_public_key(alg: &'static ECDSASigningAlgorithm,
private_key: untrusted::Input,
public_key: untrusted::Input)
-> Result<ECDSAKeyPair, error::Unspecified> {
let key_pair = ec::suite_b::key_pair_from_bytes(
alg.curve, private_key, public_key)?;
Ok(ECDSAKeyPair { key_pair, alg })
}
}
fn split_rs_fixed<'a>(
ops: &'static ScalarOps, input: &mut untrusted::Reader<'a>)
-> Result<(untrusted::Input<'a>, untrusted::Input<'a>),
error::Unspecified> {
let scalar_len = ops.scalar_bytes_len();
let r = input.skip_and_get_input(scalar_len)?;
let s = input.skip_and_get_input(scalar_len)?;
Ok((r, s))
}
fn split_rs_asn1<'a>(
_ops: &'static ScalarOps, input: &mut untrusted::Reader<'a>)
-> Result<(untrusted::Input<'a>, untrusted::Input<'a>),
error::Unspecified> {
der::nested(input, der::Tag::Sequence, error::Unspecified, |input| {
let r = der::positive_integer(input)?;
let s = der::positive_integer(input)?;
Ok((r, s))
})
}
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())
}
fn digest_scalar_(ops: &ScalarOps, digest: &[u8]) -> Scalar {
let cops = ops.common;
let num_limbs = cops.num_limbs;
let digest = if digest.len() > num_limbs * LIMB_BYTES {
&digest[..(num_limbs * LIMB_BYTES)]
} else {
digest
};
scalar_parse_big_endian_partially_reduced_variable_consttime(
cops, AllowZero::Yes, untrusted::Input::from(digest)).unwrap()
}
fn twin_mul(ops: &PrivateKeyOps, g_scalar: &Scalar, p_scalar: &Scalar,
p_xy: &(Elem<R>, Elem<R>)) -> Point {
let scaled_g = ops.point_mul_base(g_scalar);
let scaled_p = ops.point_mul(p_scalar, p_xy);
ops.common.point_sum(&scaled_g, &scaled_p)
}
#[doc(hidden)]
pub static ECDSA_P256_SHA256_FIXED_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P256,
pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
id: ECDSASigningAlgorithmID::ECDSA_P256_SHA256_FIXED_SIGNING,
};
pub static ECDSA_P256_SHA256_FIXED: ECDSAVerificationAlgorithm =
ECDSAVerificationAlgorithm {
ops: &p256::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA256,
split_rs: split_rs_fixed,
id: ECDSAVerificationAlgorithmID::ECDSA_P256_SHA256_FIXED,
};
#[doc(hidden)]
pub static ECDSA_P384_SHA384_FIXED_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P384,
pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
id: ECDSASigningAlgorithmID::ECDSA_P384_SHA384_FIXED_SIGNING,
};
pub static ECDSA_P384_SHA384_FIXED: ECDSAVerificationAlgorithm =
ECDSAVerificationAlgorithm {
ops: &p384::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_fixed,
id: ECDSAVerificationAlgorithmID::ECDSA_P384_SHA384_FIXED,
};
#[doc(hidden)]
pub static ECDSA_P256_SHA256_ASN1_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P256,
pkcs8_template: &EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE,
id: ECDSASigningAlgorithmID::ECDSA_P256_SHA256_ASN1_SIGNING,
};
pub static ECDSA_P256_SHA256_ASN1: ECDSAVerificationAlgorithm =
ECDSAVerificationAlgorithm {
ops: &p256::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA256,
split_rs: split_rs_asn1,
id: ECDSAVerificationAlgorithmID::ECDSA_P256_SHA256_ASN1,
};
pub static ECDSA_P256_SHA384_ASN1: ECDSAVerificationAlgorithm =
ECDSAVerificationAlgorithm {
ops: &p256::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_asn1,
id: ECDSAVerificationAlgorithmID::ECDSA_P256_SHA384_ASN1,
};
pub static ECDSA_P384_SHA256_ASN1: ECDSAVerificationAlgorithm =
ECDSAVerificationAlgorithm {
ops: &p384::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA256,
split_rs: split_rs_asn1,
id: ECDSAVerificationAlgorithmID::ECDSA_P384_SHA256_ASN1,
};
#[doc(hidden)]
pub static ECDSA_P384_SHA384_ASN1_SIGNING: ECDSASigningAlgorithm =
ECDSASigningAlgorithm {
curve: &ec::suite_b::curve::P384,
pkcs8_template: &EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE,
id: ECDSASigningAlgorithmID::ECDSA_P384_SHA384_ASN1_SIGNING,
};
pub static ECDSA_P384_SHA384_ASN1: ECDSAVerificationAlgorithm =
ECDSAVerificationAlgorithm {
ops: &p384::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_asn1,
id: ECDSAVerificationAlgorithmID::ECDSA_P384_SHA384_ASN1,
};
static EC_PUBLIC_KEY_P256_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
bytes: include_bytes ! ("ecPublicKey_p256_pkcs8_v1_template.der"),
alg_id_range: core::ops::Range { start: 8, end: 27 },
curve_id_index: 9,
private_key_index: 0x24,
};
static EC_PUBLIC_KEY_P384_PKCS8_V1_TEMPLATE: pkcs8::Template = pkcs8::Template {
bytes: include_bytes!("ecPublicKey_p384_pkcs8_v1_template.der"),
alg_id_range: core::ops::Range { start: 8, end: 24 },
curve_id_index: 9,
private_key_index: 0x23,
};
#[cfg(test)]
mod tests {
use {digest, test};
use super::digest_scalar_;
use super::super::ops::*;
use untrusted;
#[test]
fn ecdsa_digest_scalar_test() {
test::from_file("src/ec/suite_b/ecdsa_digest_scalar_tests.txt",
|section, test_case| {
assert_eq!(section, "");
let curve_name = test_case.consume_string("Curve");
let digest_name = test_case.consume_string("Digest");
let input = test_case.consume_bytes("Input");
let output = test_case.consume_bytes("Output");
let (ops, digest_alg) = match
(curve_name.as_str(), digest_name.as_str()) {
("P-256", "SHA256") =>
(&p256::PUBLIC_SCALAR_OPS, &digest::SHA256),
("P-256", "SHA384") =>
(&p256::PUBLIC_SCALAR_OPS, &digest::SHA384),
("P-384", "SHA256") =>
(&p384::PUBLIC_SCALAR_OPS, &digest::SHA256),
("P-384", "SHA384") =>
(&p384::PUBLIC_SCALAR_OPS, &digest::SHA384),
_ => {
panic!("Unsupported curve+digest: {}+{}", curve_name,
digest_name);
}
};
let num_limbs = ops.public_key_ops.common.num_limbs;
assert_eq!(input.len(), digest_alg.output_len);
assert_eq!(output.len(),
ops.public_key_ops.common.num_limbs * LIMB_BYTES);
let expected = scalar_parse_big_endian_variable(
ops.public_key_ops.common, AllowZero::Yes,
untrusted::Input::from(&output)).unwrap();
let actual = digest_scalar_(ops.scalar_ops, &input);
assert_eq!(actual.limbs[..num_limbs], expected.limbs[..num_limbs]);
Ok(())
});
}
}