use arithmetic::montgomery::*;
use {der, digest, error, private, signature};
use super::digest_scalar::digest_scalar;
use ec::suite_b::{ops::*, public_key::*, verify_jacobian_point_is_on_the_curve};
use untrusted;
pub struct Algorithm {
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: AlgorithmID,
}
#[derive(Debug)]
enum AlgorithmID {
ECDSA_P256_SHA256_ASN1,
ECDSA_P256_SHA256_FIXED,
ECDSA_P256_SHA384_ASN1,
ECDSA_P384_SHA256_ASN1,
ECDSA_P384_SHA384_ASN1,
ECDSA_P384_SHA384_FIXED,
}
derive_debug_from_field!(Algorithm, id);
impl signature::VerificationAlgorithm for Algorithm {
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 = {
let h = digest::digest(self.digest_alg, msg.as_slice_less_safe());
digest_scalar(scalar_ops, &h)
};
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::Sealed for Algorithm {}
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 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)
}
pub static ECDSA_P256_SHA256_FIXED: Algorithm = Algorithm {
ops: &p256::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA256,
split_rs: split_rs_fixed,
id: AlgorithmID::ECDSA_P256_SHA256_FIXED,
};
pub static ECDSA_P384_SHA384_FIXED: Algorithm = Algorithm {
ops: &p384::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_fixed,
id: AlgorithmID::ECDSA_P384_SHA384_FIXED,
};
pub static ECDSA_P256_SHA256_ASN1: Algorithm = Algorithm {
ops: &p256::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA256,
split_rs: split_rs_asn1,
id: AlgorithmID::ECDSA_P256_SHA256_ASN1,
};
pub static ECDSA_P256_SHA384_ASN1: Algorithm = Algorithm {
ops: &p256::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_asn1,
id: AlgorithmID::ECDSA_P256_SHA384_ASN1,
};
pub static ECDSA_P384_SHA256_ASN1: Algorithm = Algorithm {
ops: &p384::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA256,
split_rs: split_rs_asn1,
id: AlgorithmID::ECDSA_P384_SHA256_ASN1,
};
pub static ECDSA_P384_SHA384_ASN1: Algorithm = Algorithm {
ops: &p384::PUBLIC_SCALAR_OPS,
digest_alg: &digest::SHA384,
split_rs: split_rs_asn1,
id: AlgorithmID::ECDSA_P384_SHA384_ASN1,
};