use super::Error;
use super::curves::CurveId;
use crate::bignum::{BoxedMontModulus, BoxedUint};
use crate::ct::ConstantTimeEq;
use crate::hash::{Digest, Hmac};
use crate::rng::RngCore;
use alloc::vec;
use alloc::vec::Vec;
#[derive(Clone, Debug)]
pub struct BoxedEcdsaPublicKey {
curve: CurveId,
x: BoxedUint,
y: BoxedUint,
}
#[derive(Clone)]
pub struct BoxedEcdsaPrivateKey {
curve: CurveId,
d: BoxedUint,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BoxedEcdsaSignature {
r: BoxedUint,
s: BoxedUint,
}
#[derive(Clone)]
pub struct BoxedEcdhPrivateKey {
curve: CurveId,
d: BoxedUint,
}
fn in_range(v: &BoxedUint, n: &BoxedUint) -> bool {
!v.is_zero() && v.reduce(n) == *v
}
fn inv_mod(fm: &BoxedMontModulus, a: &BoxedUint, m: &BoxedUint) -> BoxedUint {
fm.pow(a, &m.sub(&BoxedUint::from_u64(2)))
}
fn bits2int(data: &[u8], qlen: usize) -> BoxedUint {
let blen = data.len() * 8;
let v = BoxedUint::from_be_bytes(data);
if blen > qlen {
v.shr_bits(blen - qlen)
} else {
v
}
}
fn random_scalar<R: RngCore>(curve: CurveId, n: &BoxedUint, rng: &mut R) -> BoxedUint {
let bytes = curve.order_len();
let nbits = n.bit_len();
let high_keep_bits = ((nbits - 1) % 8) + 1;
let high_mask = if high_keep_bits == 8 {
0xff
} else {
(1u8 << high_keep_bits) - 1
};
loop {
let mut buf = vec![0u8; bytes];
rng.fill_bytes(&mut buf);
buf[0] &= high_mask;
let candidate = BoxedUint::from_be_bytes(&buf);
if !candidate.is_zero() && candidate.lt(n) {
return candidate;
}
}
}
fn generate_k<D: Digest>(
d: &BoxedUint,
hash: &[u8],
n: &BoxedUint,
order_len: usize,
qlen: usize,
) -> BoxedUint {
let d_oct = d.to_be_bytes(order_len);
let h_oct = bits2int(hash, qlen).reduce(n).to_be_bytes(order_len);
let mut v = D::zeroed_output();
for b in v.as_mut() {
*b = 0x01;
}
let mut k = D::zeroed_output();
for &sep in &[0x00u8, 0x01u8] {
let mut mac = Hmac::<D>::new(k.as_ref());
mac.update(v.as_ref());
mac.update(&[sep]);
mac.update(&d_oct);
mac.update(&h_oct);
k = mac.finalize();
v = Hmac::<D>::mac(k.as_ref(), v.as_ref());
}
loop {
let mut t = Vec::with_capacity(order_len);
while t.len() < order_len {
v = Hmac::<D>::mac(k.as_ref(), v.as_ref());
t.extend_from_slice(v.as_ref());
}
let candidate = bits2int(&t[..order_len], qlen);
if in_range(&candidate, n) {
return candidate;
}
let mut mac = Hmac::<D>::new(k.as_ref());
mac.update(v.as_ref());
mac.update(&[0x00]);
k = mac.finalize();
v = Hmac::<D>::mac(k.as_ref(), v.as_ref());
}
}
impl BoxedEcdsaPublicKey {
pub fn from_sec1(curve: CurveId, bytes: &[u8]) -> Result<Self, Error> {
let flen = curve.field_len();
if bytes.len() != 1 + 2 * flen || bytes[0] != 0x04 {
return Err(Error::Malformed);
}
let x = BoxedUint::from_be_bytes(&bytes[1..1 + flen]);
let y = BoxedUint::from_be_bytes(&bytes[1 + flen..]);
let c = curve.curve();
if !c.in_field(&x) || !c.in_field(&y) || !c.is_on_curve(&x, &y) {
return Err(Error::InvalidInput);
}
Ok(BoxedEcdsaPublicKey { curve, x, y })
}
pub fn to_sec1(&self) -> Vec<u8> {
let flen = self.curve.field_len();
let mut out = vec![0u8; 1 + 2 * flen];
out[0] = 0x04;
out[1..1 + flen].copy_from_slice(&self.x.to_be_bytes(flen));
out[1 + flen..].copy_from_slice(&self.y.to_be_bytes(flen));
out
}
pub fn curve(&self) -> CurveId {
self.curve
}
pub fn verify<D: Digest>(&self, msg: &[u8], sig: &BoxedEcdsaSignature) -> Result<(), Error> {
let c = self.curve.curve();
let n = c.order().clone();
let fq = BoxedMontModulus::new(&n);
if !in_range(&sig.r, &n) || !in_range(&sig.s, &n) {
return Err(Error::Verification);
}
let hash = D::digest(msg);
let z = bits2int(hash.as_ref(), n.bit_len()).reduce(&n);
let w = inv_mod(&fq, &sig.s, &n);
let u1 = fq.mul_mod(&z, &w);
let u2 = fq.mul_mod(&sig.r, &w);
let point = c.lift_affine(&self.x, &self.y);
let sum = c.point_add(&c.mul_generator(&u1), &c.scalar_mul(&u2, &point));
let (vx, _) = c.to_affine(&sum).ok_or(Error::Verification)?;
let v = vx.reduce(&n);
if bool::from(v.ct_eq(&sig.r)) {
Ok(())
} else {
Err(Error::Verification)
}
}
}
impl BoxedEcdsaPrivateKey {
pub fn from_bytes(curve: CurveId, bytes: &[u8]) -> Result<Self, Error> {
let d = BoxedUint::from_be_bytes(bytes);
let n = curve.curve().order().clone();
if in_range(&d, &n) {
Ok(BoxedEcdsaPrivateKey { curve, d })
} else {
Err(Error::InvalidInput)
}
}
pub fn generate<R: RngCore>(curve: CurveId, rng: &mut R) -> Self {
let n = curve.curve().order().clone();
BoxedEcdsaPrivateKey {
curve,
d: random_scalar(curve, &n, rng),
}
}
pub fn curve(&self) -> CurveId {
self.curve
}
pub fn public_key(&self) -> BoxedEcdsaPublicKey {
let c = self.curve.curve();
let (x, y) = c
.to_affine(&c.mul_generator(&self.d))
.expect("d in [1,n-1] so d*G is not the identity");
BoxedEcdsaPublicKey {
curve: self.curve,
x,
y,
}
}
pub fn sign<D: Digest>(&self, msg: &[u8]) -> Result<BoxedEcdsaSignature, Error> {
let c = self.curve.curve();
let n = c.order().clone();
let fq = BoxedMontModulus::new(&n);
let order_len = self.curve.order_len();
let hash = D::digest(msg);
let z = bits2int(hash.as_ref(), n.bit_len()).reduce(&n);
let k = generate_k::<D>(&self.d, hash.as_ref(), &n, order_len, n.bit_len());
let r = c
.to_affine(&c.mul_generator(&k))
.ok_or(Error::InvalidInput)?
.0
.reduce(&n);
if r.is_zero() {
return Err(Error::InvalidInput);
}
let k_inv = inv_mod(&fq, &k, &n);
let z_rd = fq.add_mod(&z, &fq.mul_mod(&r, &self.d));
let s = fq.mul_mod(&k_inv, &z_rd);
if s.is_zero() {
return Err(Error::InvalidInput);
}
Ok(BoxedEcdsaSignature { r, s })
}
}
impl BoxedEcdsaSignature {
pub fn from_components(r: BoxedUint, s: BoxedUint) -> Self {
BoxedEcdsaSignature { r, s }
}
pub fn r(&self) -> &BoxedUint {
&self.r
}
pub fn s(&self) -> &BoxedUint {
&self.s
}
pub fn r_bytes(&self, curve: CurveId) -> Vec<u8> {
self.r.to_be_bytes(curve.order_len())
}
pub fn s_bytes(&self, curve: CurveId) -> Vec<u8> {
self.s.to_be_bytes(curve.order_len())
}
pub fn to_bytes(&self, curve: CurveId) -> Vec<u8> {
let len = curve.order_len();
let mut out = self.r.to_be_bytes(len);
out.extend_from_slice(&self.s.to_be_bytes(len));
out
}
}
#[cfg(feature = "der")]
impl BoxedEcdsaSignature {
pub fn to_der(&self, curve: CurveId) -> Vec<u8> {
use crate::der::{encode_integer, encode_sequence};
let len = curve.order_len();
encode_sequence(
&[
encode_integer(&self.r.to_be_bytes(len)),
encode_integer(&self.s.to_be_bytes(len)),
]
.concat(),
)
}
pub fn from_der(der: &[u8]) -> Result<Self, Error> {
use crate::der::Reader;
let mut reader = Reader::new(der);
let mut seq = reader.read_sequence().map_err(|_| Error::Malformed)?;
let r = seq
.read_unsigned_integer_bytes()
.map_err(|_| Error::Malformed)?;
let s = seq
.read_unsigned_integer_bytes()
.map_err(|_| Error::Malformed)?;
seq.finish().map_err(|_| Error::Malformed)?;
reader.finish().map_err(|_| Error::Malformed)?;
Ok(BoxedEcdsaSignature {
r: BoxedUint::from_be_bytes(r),
s: BoxedUint::from_be_bytes(s),
})
}
}
#[cfg(feature = "der")]
impl BoxedEcdsaPrivateKey {
pub fn to_sec1_der(&self) -> Vec<u8> {
use crate::der::{
encode_bit_string, encode_context, encode_integer, encode_octet_string,
encode_sequence, oid_tlv,
};
let order_len = self.curve.order_len();
let priv_oct = encode_octet_string(&self.d.to_be_bytes(order_len));
let params = encode_context(0, &oid_tlv(self.curve.named_curve_oid()));
let pubkey = encode_context(1, &encode_bit_string(&self.public_key().to_sec1()));
encode_sequence(&[encode_integer(&[1]), priv_oct, params, pubkey].concat())
}
pub fn to_sec1_pem(&self) -> alloc::string::String {
crate::der::pem_encode("EC PRIVATE KEY", &self.to_sec1_der())
}
pub fn from_sec1_der(der: &[u8]) -> Result<Self, Error> {
use crate::der::{Reader, parse_oid, tag};
let mut outer = Reader::new(der);
let mut seq = outer.read_sequence().map_err(|_| Error::Malformed)?;
seq.read_integer_bytes().map_err(|_| Error::Malformed)?; let priv_bytes = seq.read_octet_string().map_err(|_| Error::Malformed)?;
if seq.peek_tag() != Some(tag::context(0)) {
return Err(Error::Malformed);
}
let params = seq
.read_tlv(tag::context(0))
.map_err(|_| Error::Malformed)?;
let mut pr = Reader::new(params);
let arcs = parse_oid(pr.read_oid().map_err(|_| Error::Malformed)?)
.map_err(|_| Error::Malformed)?;
let curve = CurveId::from_named_curve_oid(&arcs).ok_or(Error::Malformed)?;
Self::from_bytes(curve, priv_bytes)
}
pub fn from_sec1_pem(pem: &str) -> Result<Self, Error> {
let der = crate::der::pem_decode(pem, "EC PRIVATE KEY").map_err(|_| Error::Malformed)?;
Self::from_sec1_der(&der)
}
}
impl BoxedEcdhPrivateKey {
pub fn generate<R: RngCore>(curve: CurveId, rng: &mut R) -> Self {
let n = curve.curve().order().clone();
BoxedEcdhPrivateKey {
curve,
d: random_scalar(curve, &n, rng),
}
}
pub fn from_bytes(curve: CurveId, bytes: &[u8]) -> Result<Self, Error> {
let d = BoxedUint::from_be_bytes(bytes);
let n = curve.curve().order().clone();
if in_range(&d, &n) {
Ok(BoxedEcdhPrivateKey { curve, d })
} else {
Err(Error::InvalidInput)
}
}
pub fn public_key(&self) -> BoxedEcdsaPublicKey {
let c = self.curve.curve();
let (x, y) = c
.to_affine(&c.mul_generator(&self.d))
.expect("d in [1,n-1] so d*G is not the identity");
BoxedEcdsaPublicKey {
curve: self.curve,
x,
y,
}
}
pub fn diffie_hellman(&self, peer: &BoxedEcdsaPublicKey) -> Result<Vec<u8>, Error> {
if peer.curve != self.curve {
return Err(Error::InvalidInput);
}
let c = self.curve.curve();
let point = c.lift_affine(&peer.x, &peer.y);
let shared = c.scalar_mul(&self.d, &point);
let (x, _) = c.to_affine(&shared).ok_or(Error::InvalidInput)?;
Ok(x.to_be_bytes(self.curve.field_len()))
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::hash::{Sha256, Sha384, Sha512};
use crate::rng::HmacDrbg;
fn from_hex(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap())
.collect()
}
#[test]
fn rfc6979_p256_sample() {
let d = from_hex("c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721");
let sk = BoxedEcdsaPrivateKey::from_bytes(CurveId::P256, &d).unwrap();
let sig = sk.sign::<Sha256>(b"sample").unwrap();
assert_eq!(
sig.r.to_be_bytes(32),
from_hex("efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716")
);
assert_eq!(
sig.s.to_be_bytes(32),
from_hex("f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8")
);
sk.public_key().verify::<Sha256>(b"sample", &sig).unwrap();
}
#[test]
fn rfc6979_p384_sample() {
let d = from_hex(
"6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d8\
96d5724e4c70a825f872c9ea60d2edf5",
);
let sk = BoxedEcdsaPrivateKey::from_bytes(CurveId::P384, &d).unwrap();
let sig = sk.sign::<Sha384>(b"sample").unwrap();
assert_eq!(
sig.r.to_be_bytes(48),
from_hex(
"94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c\
81a648152e44acf96e36dd1e80fabe46"
)
);
assert_eq!(
sig.s.to_be_bytes(48),
from_hex(
"99ef4aeb15f178cea1fe40db2603138f130e740a19624526203b6351d0a3a94f\
a329c145786e679e7b82c71a38628ac8"
)
);
sk.public_key().verify::<Sha384>(b"sample", &sig).unwrap();
}
#[test]
fn rfc6979_p521_sample() {
let d = from_hex(
"00fad06daa62ba3b25d2fb40133da757205de67f5bb0018fee8c86e1b68c7e75\
caa896eb32f1f47c70855836a6d16fcc1466f6d8fbec67db89ec0c08b0e996b8\
3538",
);
let sk = BoxedEcdsaPrivateKey::from_bytes(CurveId::P521, &d).unwrap();
let sig = sk.sign::<Sha512>(b"sample").unwrap();
assert_eq!(
sig.r.to_be_bytes(66),
from_hex(
"00c328fafcbd79dd77850370c46325d987cb525569fb63c5d3bc53950e6d4c5f\
174e25a1ee9017b5d450606add152b534931d7d4e8455cc91f9b15bf05ec36e3\
77fa"
)
);
sk.public_key().verify::<Sha512>(b"sample", &sig).unwrap();
}
#[test]
fn secp256k1_sign_verify_roundtrip() {
let mut rng = HmacDrbg::<Sha256>::new(b"secp256k1-key", b"nonce", &[]);
let sk = BoxedEcdsaPrivateKey::generate(CurveId::Secp256k1, &mut rng);
let pk = sk.public_key();
let sig = sk.sign::<Sha256>(b"hello secp256k1").unwrap();
pk.verify::<Sha256>(b"hello secp256k1", &sig).unwrap();
assert!(pk.verify::<Sha256>(b"tampered", &sig).is_err());
let sec1 = pk.to_sec1();
assert_eq!(
BoxedEcdsaPublicKey::from_sec1(CurveId::Secp256k1, &sec1)
.unwrap()
.to_sec1(),
sec1
);
}
#[cfg(feature = "der")]
#[test]
fn ec_private_key_sec1_roundtrip() {
for curve in [
CurveId::P256,
CurveId::P384,
CurveId::P521,
CurveId::Secp256k1,
] {
let mut rng = HmacDrbg::<Sha256>::new(b"sec1", b"n", &[]);
let sk = BoxedEcdsaPrivateKey::generate(curve, &mut rng);
let pem = sk.to_sec1_pem();
assert!(pem.starts_with("-----BEGIN EC PRIVATE KEY-----"));
let parsed = BoxedEcdsaPrivateKey::from_sec1_pem(&pem).unwrap();
assert_eq!(parsed.curve(), curve);
assert_eq!(parsed.public_key().to_sec1(), sk.public_key().to_sec1());
}
}
#[test]
fn ecdh_p256_matches_const_generic() {
let mut rng = HmacDrbg::<Sha256>::new(b"ecdh", b"n", &[]);
let a = BoxedEcdhPrivateKey::generate(CurveId::P256, &mut rng);
let b = BoxedEcdhPrivateKey::generate(CurveId::P256, &mut rng);
let ab = a.diffie_hellman(&b.public_key()).unwrap();
let ba = b.diffie_hellman(&a.public_key()).unwrap();
assert_eq!(ab, ba);
}
#[test]
fn boxed_signature_r_s_accessors_roundtrip() {
let mut rng = HmacDrbg::<Sha256>::new(b"sig-rs", b"n", &[]);
for curve in [
CurveId::P256,
CurveId::P384,
CurveId::P521,
CurveId::Secp256k1,
] {
let sk = BoxedEcdsaPrivateKey::generate(curve, &mut rng);
let sig = sk.sign::<Sha256>(b"hello").unwrap();
let rebuilt = BoxedEcdsaSignature::from_components(sig.r().clone(), sig.s().clone());
assert_eq!(rebuilt, sig);
let mut concat = sig.r_bytes(curve);
concat.extend_from_slice(&sig.s_bytes(curve));
assert_eq!(concat, sig.to_bytes(curve));
}
}
}