Swap `ring` for our own in-crate ECDSA validator
authorMatt Corallo <git@bluematt.me>
Mon, 4 Mar 2024 03:22:08 +0000 (03:22 +0000)
committerMatt Corallo <git@bluematt.me>
Wed, 3 Apr 2024 09:15:18 +0000 (09:15 +0000)
While `ring` is great, it struggles with platform support and has a
fairly involved dependency tree due to its reliance on C backends.

Further, while the `RustCrypto` org tries to stick to Rust, in
doing so it takes on more (unnecessary) dependencies and has a
particularly unusable MSRV policy. Finally, its contributor base
has historically not been particularly friendly.

Thus, sadly, there's not really a good option for doing ECDSA (non-
secp256k1) validation using a third-party crate.

Instead, we go our own way here, adding an in-crate ECDSA
validator over secp{256,384}r1.

This also adds a new bench, showing our secp256r1 validation is,
sadly, something like 50x slower than OpenSSL.

Cargo.toml
bench/benches/bench.rs
src/crypto/ec.rs [new file with mode: 0644]
src/crypto/mod.rs
src/crypto/secp256r1.rs [new file with mode: 0644]
src/crypto/secp384r1.rs [new file with mode: 0644]
src/validation.rs

index 7dd5d41458889d81dc9a9944673bd9c9ee6c5b30..cab20d7a4db001085233d60c415776c8fa71861b 100644 (file)
@@ -16,12 +16,11 @@ features = ["std", "validation", "tokio"]
 [features]
 default = ["validation"]
 std = []
-validation = ["bitcoin_hashes", "ring", "hex_lit"]
+validation = ["bitcoin_hashes", "hex_lit"]
 tokio = ["tokio_crate/net", "tokio_crate/io-util", "std"]
 build_server = ["tokio", "tokio_crate/rt-multi-thread", "tokio_crate/macros"]
 
 [dependencies]
-ring = { version = "0.17", default-features = false, features = ["alloc"], optional = true }
 bitcoin_hashes = { version = "0.14", default-features = false, optional = true }
 hex_lit = { version = "0.1", default-features = false, features = ["rust_v_1_46"], optional = true }
 tokio_crate = { package = "tokio", version = "1.0", default-features = false, optional = true }
index 2d963bc5aaa10653b04dfec699fe5370e09f12ef..4b5d6e4189f84b33f7ad030ead627f64a9e569a7 100644 (file)
@@ -1,6 +1,7 @@
 use criterion::{Criterion, criterion_group, criterion_main};
 
 use dnssec_prover::crypto::rsa::validate_rsa;
+use dnssec_prover::crypto::secp256r1;
 
 pub fn bench_validate_rsa(bench: &mut Criterion) {
        // A signature by the root key over the root DNSKEY RRSet
@@ -12,7 +13,18 @@ pub fn bench_validate_rsa(bench: &mut Criterion) {
        }));
 }
 
+pub fn bench_validate_secp256r1(bench: &mut Criterion) {
+       // A signature by the com. key over the com. DNSKEY RRSet
+       let pk = [183, 31, 4, 101, 16, 29, 219, 226, 191, 12, 148, 85, 209, 47, 161, 108, 28, 218, 68, 244, 191, 27, 162, 85, 52, 24, 173, 31, 58, 169, 176, 105, 115, 242, 27, 132, 235, 83, 44, 244, 3, 94, 232, 212, 131, 44, 162, 109, 137, 48, 106, 125, 50, 86, 12, 12, 176, 18, 157, 69, 10, 193, 8, 53];
+       let sig = [207, 89, 121, 239, 214, 5, 201, 157, 91, 15, 126, 57, 251, 60, 13, 82, 33, 137, 67, 212, 128, 161, 32, 93, 133, 247, 165, 154, 143, 126, 112, 177, 71, 23, 220, 48, 182, 191, 235, 38, 123, 7, 183, 244, 255, 239, 156, 194, 199, 48, 23, 100, 97, 240, 232, 81, 92, 31, 150, 66, 123, 249, 135, 224];
+       let rrset_hash = [148, 51, 14, 246, 192, 70, 100, 66, 31, 67, 171, 251, 100, 195, 224, 203, 145, 160, 238, 28, 26, 167, 115, 82, 17, 58, 83, 114, 97, 53, 182, 30];
+       bench.bench_function("validate_secp256r1", |b| b.iter(|| {
+               secp256r1::validate_ecdsa(&pk, &sig, &rrset_hash).unwrap();
+       }));
+}
+
 criterion_group!(benches,
        bench_validate_rsa,
+       bench_validate_secp256r1,
 );
 criterion_main!(benches);
diff --git a/src/crypto/ec.rs b/src/crypto/ec.rs
new file mode 100644 (file)
index 0000000..bd8c5cf
--- /dev/null
@@ -0,0 +1,292 @@
+//! Simple verification of ECDSA signatures over SECP Random curves
+
+use super::bigint::*;
+
+pub(super) trait IntMod: Clone + Eq + Sized {
+       type I: Int;
+       fn from_i(v: Self::I) -> Self;
+       fn from_modinv_of(v: Self::I) -> Result<Self, ()>;
+
+       const ZERO: Self;
+       const ONE: Self;
+
+       fn mul(&self, o: &Self) -> Self;
+       fn square(&self) -> Self;
+       fn add(&self, o: &Self) -> Self;
+       fn sub(&self, o: &Self) -> Self;
+       fn double(&self) -> Self;
+       fn times_three(&self) -> Self;
+       fn times_four(&self) -> Self;
+       fn times_eight(&self) -> Self;
+
+       fn into_i(self) -> Self::I;
+}
+impl<M: PrimeModulus<U256> + Clone + Eq> IntMod for U256Mod<M> {
+       type I = U256;
+       fn from_i(v: Self::I) -> Self { U256Mod::from_u256(v) }
+       fn from_modinv_of(v: Self::I) -> Result<Self, ()> { U256Mod::from_modinv_of(v) }
+
+       const ZERO: Self = U256Mod::<M>::from_u256_panicking(U256::zero());
+       const ONE: Self = U256Mod::<M>::from_u256_panicking(U256::one());
+
+       fn mul(&self, o: &Self) -> Self { self.mul(o) }
+       fn square(&self) -> Self { self.square() }
+       fn add(&self, o: &Self) -> Self { self.add(o) }
+       fn sub(&self, o: &Self) -> Self { self.sub(o) }
+       fn double(&self) -> Self { self.double() }
+       fn times_three(&self) -> Self { self.times_three() }
+       fn times_four(&self) -> Self { self.times_four() }
+       fn times_eight(&self) -> Self { self.times_eight() }
+
+       fn into_i(self) -> Self::I { self.into_u256() }
+}
+impl<M: PrimeModulus<U384> + Clone + Eq> IntMod for U384Mod<M> {
+       type I = U384;
+       fn from_i(v: Self::I) -> Self { U384Mod::from_u384(v) }
+       fn from_modinv_of(v: Self::I) -> Result<Self, ()> { U384Mod::from_modinv_of(v) }
+
+       const ZERO: Self = U384Mod::<M>::from_u384_panicking(U384::zero());
+       const ONE: Self = U384Mod::<M>::from_u384_panicking(U384::one());
+
+       fn mul(&self, o: &Self) -> Self { self.mul(o) }
+       fn square(&self) -> Self { self.square() }
+       fn add(&self, o: &Self) -> Self { self.add(o) }
+       fn sub(&self, o: &Self) -> Self { self.sub(o) }
+       fn double(&self) -> Self { self.double() }
+       fn times_three(&self) -> Self { self.times_three() }
+       fn times_four(&self) -> Self { self.times_four() }
+       fn times_eight(&self) -> Self { self.times_eight() }
+
+       fn into_i(self) -> Self::I { self.into_u384() }
+}
+
+pub(super) trait Curve : Copy {
+       type Int: Int;
+
+       // With const generics, both IntModP and IntModN can be replaced with a single IntMod.
+       type IntModP: IntMod<I = Self::Int>;
+       type IntModN: IntMod<I = Self::Int>;
+
+       type P: PrimeModulus<Self::Int>;
+       type N: PrimeModulus<Self::Int>;
+
+       // Curve parameters y^2 = x^3 + ax + b
+       const A: Self::IntModP;
+       const B: Self::IntModP;
+
+       const G: Point<Self>;
+}
+
+#[derive(Clone, PartialEq, Eq)]
+pub(super) struct Point<C: Curve + ?Sized> {
+       x: C::IntModP,
+       y: C::IntModP,
+       z: C::IntModP,
+}
+
+impl<C: Curve + ?Sized> Point<C> {
+       fn on_curve(x: &C::IntModP, y: &C::IntModP) -> Result<(), ()> {
+               let x_2 = x.square();
+               let x_3 = x_2.mul(&x);
+               let v = x_3.add(&C::A.mul(&x)).add(&C::B);
+
+               let y_2 = y.square();
+               if y_2 != v {
+                       Err(())
+               } else {
+                       Ok(())
+               }
+       }
+
+       #[cfg(debug_assertions)]
+       fn on_curve_z(x: &C::IntModP, y: &C::IntModP, z: &C::IntModP) -> Result<(), ()> {
+               let m = C::IntModP::from_modinv_of(z.clone().into_i())?;
+               let m_2 = m.square();
+               let m_3 = m_2.mul(&m);
+               let x_norm = x.mul(&m_2);
+               let y_norm = y.mul(&m_3);
+               Self::on_curve(&x_norm, &y_norm)
+       }
+
+       #[cfg(test)]
+       fn normalize_x(&self) -> Result<C::IntModP, ()> {
+               let m = C::IntModP::from_modinv_of(self.z.clone().into_i())?;
+               Ok(self.x.mul(&m.square()))
+       }
+
+       fn from_xy(x: C::Int, y: C::Int) -> Result<Self, ()> {
+               let x = C::IntModP::from_i(x);
+               let y = C::IntModP::from_i(y);
+               Self::on_curve(&x, &y)?;
+               Ok(Point { x, y, z: C::IntModP::ONE })
+       }
+
+       pub(super) const fn from_xy_assuming_on_curve(x: C::IntModP, y: C::IntModP) -> Self {
+               Point { x, y, z: C::IntModP::ONE }
+       }
+
+       /// Checks that `expected_x` is equal to our X affine coordinate (without modular inversion).
+       fn eq_x(&self, expected_x: &C::IntModN) -> Result<(), ()> {
+               debug_assert!(expected_x.clone().into_i() < C::P::PRIME, "N is < P");
+
+               // If x is between N and P the below calculations will fail and we'll spuriously reject a
+               // signature and the wycheproof tests will fail. We should in theory accept such
+               // signatures, but the probability of this happening at random is roughly 1/2^128, i.e. we
+               // really don't need to handle it in practice. Thus, we only bother to do this in tests.
+               #[allow(unused_mut, unused_assignments)]
+               let mut slow_check = None;
+               #[cfg(test)] {
+                       slow_check = Some(C::IntModN::from_i(self.normalize_x()?.into_i()) == *expected_x);
+               }
+
+               let e: C::IntModP = C::IntModP::from_i(expected_x.clone().into_i());
+               if self.z == C::IntModP::ZERO { return Err(()); }
+               let ezz = e.mul(&self.z).mul(&self.z);
+               if self.x == ezz { Ok(()) } else {
+                       if slow_check == Some(true) { Ok(()) } else { Err(()) }
+               }
+       }
+
+       fn double(&self) -> Result<Self, ()> {
+               if self.y == C::IntModP::ZERO { return Err(()); }
+               if self.z == C::IntModP::ZERO { return Err(()); }
+
+               let s = self.x.times_four().mul(&self.y.square());
+               let z_2 = self.z.square();
+               let z_4 = z_2.square();
+               let y_2 = self.y.square();
+               let y_4 = y_2.square();
+               let x_2 = self.x.square();
+               let m = x_2.times_three().add(&C::A.mul(&z_4));
+               let x = m.square().sub(&s.double());
+               let y = m.mul(&s.sub(&x)).sub(&y_4.times_eight());
+               let z = self.y.double().mul(&self.z);
+
+               #[cfg(debug_assertions)] { assert!(Self::on_curve_z(&x, &y, &z).is_ok()); }
+               Ok(Point { x, y, z })
+       }
+
+       fn add(&self, o: &Self) -> Result<Self, ()> {
+               let o_z_2 = o.z.square();
+               let self_z_2 = self.z.square();
+
+               let u1 = self.x.mul(&o_z_2);
+               let u2 = o.x.mul(&self_z_2);
+               let s1 = self.y.mul(&o.z.mul(&o_z_2));
+               let s2 = o.y.mul(&self.z.mul(&self_z_2));
+               if u1 == u2 {
+                       if s1 != s2 { /* PAI */ return Err(()); }
+                       return self.double();
+               }
+               let h = u2.sub(&u1);
+               let h_2 = h.square();
+               let h_3 = h.mul(&h_2);
+               let r = s2.sub(&s1);
+               let x = r.square().sub(&h_3).sub(&u1.double().mul(&h_2));
+               let y = r.mul(&u1.mul(&h_2).sub(&x)).sub(&s1.mul(&h_3));
+               let z = h.mul(&self.z).mul(&o.z);
+
+               #[cfg(debug_assertions)] { assert!(Self::on_curve_z(&x, &y, &z).is_ok()); }
+               Ok(Point { x, y, z})
+       }
+}
+
+/// Calculates i * I + j * J
+#[allow(non_snake_case)]
+fn add_two_mul<C: Curve>(i: C::IntModN, I: &Point<C>, j: C::IntModN, J: &Point<C>) -> Result<Point<C>, ()> {
+       let i = i.into_i();
+       let j = j.into_i();
+
+       if i == C::Int::ZERO { /* Infinity */ return Err(()); }
+       if j == C::Int::ZERO { /* Infinity */ return Err(()); }
+
+       let mut res_opt: Result<Point<C>, ()> = Err(());
+       let i_limbs = i.limbs();
+       let j_limbs = j.limbs();
+       let mut skip_limb = 0;
+       let mut limbs_skip_iter = i_limbs.iter().zip(j_limbs.iter());
+       while limbs_skip_iter.next() == Some((&0, &0)) {
+               skip_limb += 1;
+       }
+       for (idx, (il, jl)) in i_limbs.iter().zip(j_limbs.iter()).skip(skip_limb).enumerate() {
+               let start_bit = if idx == 0 {
+                       core::cmp::min(il.leading_zeros(), jl.leading_zeros())
+               } else { 0 };
+               for b in start_bit..64 {
+                       let i_bit = (*il & (1 << (63 - b))) != 0;
+                       let j_bit = (*jl & (1 << (63 - b))) != 0;
+                       if let Ok(res) = res_opt.as_mut() {
+                               *res = res.double()?;
+                       }
+                       if i_bit {
+                               if let Ok(res) = res_opt.as_mut() {
+                                       // The wycheproof tests expect to see signatures pass even if we hit PAI on an
+                                       // intermediate result. While that's fine, I'm too lazy to go figure out if all
+                                       // our PAI definitions are right and the probability of this happening at
+                                       // random is, basically, the probability of guessing a private key anyway, so
+                                       // its not really worth actually handling outside of tests.
+                                       #[cfg(test)] {
+                                               res_opt = res.add(I);
+                                       }
+                                       #[cfg(not(test))] {
+                                               *res = res.add(I)?;
+                                       }
+                               } else {
+                                       res_opt = Ok(I.clone());
+                               }
+                       }
+                       if j_bit {
+                               if let Ok(res) = res_opt.as_mut() {
+                                       // The wycheproof tests expect to see signatures pass even if we hit PAI on an
+                                       // intermediate result. While that's fine, I'm too lazy to go figure out if all
+                                       // our PAI definitions are right and the probability of this happening at
+                                       // random is, basically, the probability of guessing a private key anyway, so
+                                       // its not really worth actually handling outside of tests.
+                                       #[cfg(test)] {
+                                               res_opt = res.add(J);
+                                       }
+                                       #[cfg(not(test))] {
+                                               *res = res.add(J)?;
+                                       }
+                               } else {
+                                       res_opt = Ok(J.clone());
+                               }
+                       }
+               }
+       }
+       res_opt
+}
+
+/// Validates the given signature against the given public key and message digest.
+pub(super) fn validate_ecdsa<C: Curve>(pk: &[u8], sig: &[u8], hash_input: &[u8]) -> Result<(), ()> {
+       #![allow(non_snake_case)]
+
+       if pk.len() != C::Int::BYTES * 2 { return Err(()); }
+       if sig.len() != C::Int::BYTES * 2 { return Err(()); }
+
+       let (r_bytes, s_bytes) = sig.split_at(C::Int::BYTES);
+       let (pk_x_bytes, pk_y_bytes) = pk.split_at(C::Int::BYTES);
+
+       let pk_x = C::Int::from_be_bytes(pk_x_bytes)?;
+       let pk_y = C::Int::from_be_bytes(pk_y_bytes)?;
+       let PK = Point::from_xy(pk_x, pk_y)?;
+
+       // from_i and from_modinv_of both will simply mod if the value is out of range. While its
+       // perfectly safe to do so, the wycheproof tests expect such signatures to be rejected, so we
+       // do so here.
+       let r_u256 = C::Int::from_be_bytes(r_bytes)?;
+       if r_u256 > C::N::PRIME { return Err(()); }
+       let s_u256 = C::Int::from_be_bytes(s_bytes)?;
+       if s_u256 > C::N::PRIME { return Err(()); }
+
+       let r = C::IntModN::from_i(r_u256);
+       let s_inv = C::IntModN::from_modinv_of(s_u256)?;
+
+       let z = C::IntModN::from_i(C::Int::from_be_bytes(hash_input)?);
+
+       let u_a = z.mul(&s_inv);
+       let u_b = r.mul(&s_inv);
+
+       let V = add_two_mul(u_a, &C::G, u_b, &PK)?;
+       V.eq_x(&r)
+}
index 24f6121a24513cbe687f17c617d7613dd9403662..eaeeb92409343cbb0bc7a4c753398d16d54cc28a 100644 (file)
@@ -20,5 +20,8 @@
 //! policy. Thus we go ahead and use that for our hashing needs.
 
 pub mod bigint;
+mod ec;
 pub mod hash;
 pub mod rsa;
+pub mod secp256r1;
+pub mod secp384r1;
diff --git a/src/crypto/secp256r1.rs b/src/crypto/secp256r1.rs
new file mode 100644 (file)
index 0000000..41d4851
--- /dev/null
@@ -0,0 +1,54 @@
+//! secp256r1 validation for DNSSEC signatures
+
+use super::bigint::*;
+use super::ec;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+struct P();
+impl PrimeModulus<U256> for P {
+       const PRIME: U256 = U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"));
+       const R_SQUARED_MOD_PRIME: U256 = U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "00000004fffffffdfffffffffffffffefffffffbffffffff0000000000000003"));
+       const NEGATIVE_PRIME_INV_MOD_R: U256 = U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "ffffffff00000002000000000000000000000001000000000000000000000001"));
+}
+#[derive(Clone, Copy, PartialEq, Eq)]
+struct N();
+impl PrimeModulus<U256> for N {
+       const PRIME: U256 = U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"));
+       const R_SQUARED_MOD_PRIME: U256 = U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "66e12d94f3d956202845b2392b6bec594699799c49bd6fa683244c95be79eea2"));
+       const NEGATIVE_PRIME_INV_MOD_R: U256 = U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "60d06633a9d6281c50fe77ecc588c6f648c944087d74d2e4ccd1c8aaee00bc4f"));
+}
+
+#[derive(Clone, Copy)]
+struct P256();
+
+impl ec::Curve for P256 {
+       type Int = U256;
+       type IntModP = U256Mod<P>;
+       type IntModN = U256Mod<N>;
+
+       type P = P;
+       type N = N;
+
+       const A: U256Mod<P> = U256Mod::from_u256_panicking(U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "ffffffff00000001000000000000000000000000fffffffffffffffffffffffc")));
+       const B: U256Mod<P> = U256Mod::from_u256_panicking(U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+               "5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b")));
+
+       const G: ec::Point<P256> = ec::Point::from_xy_assuming_on_curve(
+               U256Mod::from_u256_panicking(U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+                       "6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296"))),
+               U256Mod::from_u256_panicking(U256::from_32_be_bytes_panicking(&hex_lit::hex!(
+                       "4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5"))),
+       );
+}
+
+/// Validates the given signature against the given public key and message digest.
+pub fn validate_ecdsa(pk: &[u8], sig: &[u8], hash_input: &[u8]) -> Result<(), ()> {
+       ec::validate_ecdsa::<P256>(pk, sig, hash_input)
+}
diff --git a/src/crypto/secp384r1.rs b/src/crypto/secp384r1.rs
new file mode 100644 (file)
index 0000000..9c99ad3
--- /dev/null
@@ -0,0 +1,54 @@
+//! secp384r1 validation for DNSSEC signatures
+
+use super::bigint::*;
+use super::ec;
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+struct P();
+impl PrimeModulus<U384> for P {
+       const PRIME: U384 = U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff"));
+       const R_SQUARED_MOD_PRIME: U384 = U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "000000000000000000000000000000010000000200000000fffffffe000000000000000200000000fffffffe00000001"));
+       const NEGATIVE_PRIME_INV_MOD_R: U384 = U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "00000014000000140000000c00000002fffffffcfffffffafffffffbfffffffe00000000000000010000000100000001"));
+}
+#[derive(Clone, Copy, PartialEq, Eq)]
+struct N();
+impl PrimeModulus<U384> for N {
+       const PRIME: U384 = U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973"));
+       const R_SQUARED_MOD_PRIME: U384 = U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "0c84ee012b39bf213fb05b7a28266895d40d49174aab1cc5bc3e483afcb82947ff3d81e5df1aa4192d319b2419b409a9"));
+       const NEGATIVE_PRIME_INV_MOD_R: U384 = U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "355ca87de39dbb1fa150206ce4f194ac78d4ba5866d61787ee6c8e3df45624ce54a885995d20bb2b6ed46089e88fdc45"));
+}
+
+#[derive(Clone, Copy)]
+struct P384();
+
+impl ec::Curve for P384 {
+       type Int = U384;
+       type IntModP = U384Mod<P>;
+       type IntModN = U384Mod<N>;
+
+       type P = P;
+       type N = N;
+
+       const A: U384Mod<P> = U384Mod::from_u384_panicking(U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000fffffffc")));
+       const B: U384Mod<P> = U384Mod::from_u384_panicking(U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+               "b3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef")));
+
+       const G: ec::Point<P384> = ec::Point::from_xy_assuming_on_curve(
+               U384Mod::from_u384_panicking(U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+                       "aa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760ab7"))),
+               U384Mod::from_u384_panicking(U384::from_48_be_bytes_panicking(&hex_lit::hex!(
+                       "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f"))),
+       );
+}
+
+/// Validates the given signature against the given public key and message digest.
+pub fn validate_ecdsa(pk: &[u8], sig: &[u8], hash_input: &[u8]) -> Result<(), ()> {
+       ec::validate_ecdsa::<P384>(pk, sig, hash_input)
+}
index 2fb97448d7f0f65e870cfd15481676f08dad84cb..692bb63fa7236d4b8e4919c6eb9bc8df58beebb9 100644 (file)
@@ -5,8 +5,6 @@ use alloc::vec::Vec;
 use alloc::vec;
 use core::cmp::{self, Ordering};
 
-use ring::signature;
-
 use crate::base32;
 use crate::crypto;
 use crate::rr::*;
@@ -106,34 +104,16 @@ where Keys: IntoIterator<Item = &'a DnsKey> {
                                record.write_u16_len_prefixed_data(&mut signed_data);
                        }
 
+                       hash_ctx.update(&signed_data);
+                       let hash = hash_ctx.finish();
                        let sig_validation = match sig.alg {
-                               8|10 => {
-                                       hash_ctx.update(&signed_data);
-                                       let hash = hash_ctx.finish();
-                                       crypto::rsa::validate_rsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
-                                               .map_err(|_| ValidationError::Invalid)
-                               },
-                               13|14 => {
-                                       let alg = if sig.alg == 13 {
-                                               &signature::ECDSA_P256_SHA256_FIXED
-                                       } else {
-                                               &signature::ECDSA_P384_SHA384_FIXED
-                                       };
-
-                                       // Add 0x4 identifier to the ECDSA pubkey as expected by ring.
-                                       let mut key = Vec::with_capacity(dnskey.pubkey.len() + 1);
-                                       key.push(0x4);
-                                       key.extend_from_slice(&dnskey.pubkey);
-
-                                       signature::UnparsedPublicKey::new(alg, &key)
-                                               .verify(&signed_data, &sig.signature)
-                                               .map_err(|_| ValidationError::Invalid)
-                               },
-                               15 => {
-                                       signature::UnparsedPublicKey::new(&signature::ED25519, &dnskey.pubkey)
-                                               .verify(&signed_data, &sig.signature)
-                                               .map_err(|_| ValidationError::Invalid)
-                               },
+                               8|10 => crypto::rsa::validate_rsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
+                                       .map_err(|_| ValidationError::Invalid),
+                               13 => crypto::secp256r1::validate_ecdsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
+                                       .map_err(|_| ValidationError::Invalid),
+                               14 => crypto::secp384r1::validate_ecdsa(&dnskey.pubkey, &sig.signature, hash.as_ref())
+                                       .map_err(|_| ValidationError::Invalid),
+                               // TODO: 15 => ED25519
                                _ => return Err(ValidationError::UnsupportedAlgorithm),
                        };
                        #[cfg(fuzzing)] {