chore: checkpoint before Python removal

This commit is contained in:
2026-03-26 22:33:59 +00:00
parent 683cec9307
commit e568ddf82a
29972 changed files with 11269302 additions and 2 deletions

464
vendor/ecdsa/src/der.rs vendored Normal file
View File

@@ -0,0 +1,464 @@
//! Support for ASN.1 DER-encoded ECDSA signatures as specified in
//! [RFC5912 Appendix A].
//!
//! [RFC5912 Appendix A]: https://www.rfc-editor.org/rfc/rfc5912#appendix-A
use crate::{Error, Result};
use core::{
fmt::{self, Debug},
ops::{Add, Range},
};
use der::{asn1::UintRef, Decode, Encode, FixedTag, Length, Reader, Tag, Writer};
use elliptic_curve::{
consts::U9,
generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
FieldBytesSize, PrimeCurve,
};
#[cfg(feature = "alloc")]
use {
alloc::{boxed::Box, vec::Vec},
signature::SignatureEncoding,
spki::{der::asn1::BitString, SignatureBitStringEncoding},
};
#[cfg(feature = "serde")]
use serdect::serde::{de, ser, Deserialize, Serialize};
/// Maximum overhead of an ASN.1 DER-encoded ECDSA signature for a given curve:
/// 9-bytes.
///
/// Includes 3-byte ASN.1 DER header:
///
/// - 1-byte: ASN.1 `SEQUENCE` tag (0x30)
/// - 2-byte: length
///
/// ...followed by two ASN.1 `INTEGER` values, which each have a header whose
/// maximum length is the following:
///
/// - 1-byte: ASN.1 `INTEGER` tag (0x02)
/// - 1-byte: length
/// - 1-byte: zero to indicate value is positive (`INTEGER` is signed)
pub type MaxOverhead = U9;
/// Maximum size of an ASN.1 DER encoded signature for the given elliptic curve.
pub type MaxSize<C> = <<FieldBytesSize<C> as Add>::Output as Add<MaxOverhead>>::Output;
/// Byte array containing a serialized ASN.1 signature
type SignatureBytes<C> = GenericArray<u8, MaxSize<C>>;
/// ASN.1 DER-encoded signature as specified in [RFC5912 Appendix A]:
///
/// ```text
/// ECDSA-Sig-Value ::= SEQUENCE {
/// r INTEGER,
/// s INTEGER
/// }
/// ```
///
/// [RFC5912 Appendix A]: https://www.rfc-editor.org/rfc/rfc5912#appendix-A
pub struct Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
/// ASN.1 DER-encoded signature data
bytes: SignatureBytes<C>,
/// Range of the `r` value within the signature
r_range: Range<usize>,
/// Range of the `s` value within the signature
s_range: Range<usize>,
}
#[allow(clippy::len_without_is_empty)]
impl<C> Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
/// Parse signature from DER-encoded bytes.
pub fn from_bytes(input: &[u8]) -> Result<Self> {
let (r, s) = decode_der(input).map_err(|_| Error::new())?;
if r.as_bytes().len() > C::FieldBytesSize::USIZE
|| s.as_bytes().len() > C::FieldBytesSize::USIZE
{
return Err(Error::new());
}
let r_range = find_scalar_range(input, r.as_bytes())?;
let s_range = find_scalar_range(input, s.as_bytes())?;
if s_range.end != input.len() {
return Err(Error::new());
}
let mut bytes = SignatureBytes::<C>::default();
bytes[..s_range.end].copy_from_slice(input);
Ok(Signature {
bytes,
r_range,
s_range,
})
}
/// Create an ASN.1 DER encoded signature from big endian `r` and `s` scalar
/// components.
pub(crate) fn from_components(r: &[u8], s: &[u8]) -> der::Result<Self> {
let r = UintRef::new(r)?;
let s = UintRef::new(s)?;
let mut bytes = SignatureBytes::<C>::default();
let mut writer = der::SliceWriter::new(&mut bytes);
writer.sequence((r.encoded_len()? + s.encoded_len()?)?, |seq| {
seq.encode(&r)?;
seq.encode(&s)
})?;
writer
.finish()?
.try_into()
.map_err(|_| der::Tag::Sequence.value_error())
}
/// Borrow this signature as a byte slice
pub fn as_bytes(&self) -> &[u8] {
&self.bytes.as_slice()[..self.len()]
}
/// Serialize this signature as a boxed byte slice
#[cfg(feature = "alloc")]
pub fn to_bytes(&self) -> Box<[u8]> {
self.as_bytes().to_vec().into_boxed_slice()
}
/// Get the length of the signature in bytes
pub fn len(&self) -> usize {
self.s_range.end
}
/// Get the `r` component of the signature (leading zeros removed)
pub(crate) fn r(&self) -> &[u8] {
&self.bytes[self.r_range.clone()]
}
/// Get the `s` component of the signature (leading zeros removed)
pub(crate) fn s(&self) -> &[u8] {
&self.bytes[self.s_range.clone()]
}
}
impl<C> AsRef<[u8]> for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
impl<C> Clone for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn clone(&self) -> Self {
Self {
bytes: self.bytes.clone(),
r_range: self.r_range.clone(),
s_range: self.s_range.clone(),
}
}
}
impl<C> Debug for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ecdsa::der::Signature<{:?}>(", C::default())?;
for &byte in self.as_ref() {
write!(f, "{:02X}", byte)?;
}
write!(f, ")")
}
}
impl<'a, C> Decode<'a> for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn decode<R: Reader<'a>>(reader: &mut R) -> der::Result<Self> {
let header = reader.peek_header()?;
header.tag.assert_eq(Tag::Sequence)?;
let mut buf = SignatureBytes::<C>::default();
let len = (header.encoded_len()? + header.length)?;
let slice = buf
.get_mut(..usize::try_from(len)?)
.ok_or_else(|| reader.error(Tag::Sequence.length_error().kind()))?;
reader.read_into(slice)?;
Self::from_bytes(slice).map_err(|_| Tag::Integer.value_error())
}
}
impl<C> Encode for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn encoded_len(&self) -> der::Result<Length> {
Length::try_from(self.len())
}
fn encode(&self, writer: &mut impl Writer) -> der::Result<()> {
writer.write(self.as_bytes())
}
}
impl<C> FixedTag for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
const TAG: Tag = Tag::Sequence;
}
impl<C> From<crate::Signature<C>> for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn from(sig: crate::Signature<C>) -> Signature<C> {
sig.to_der()
}
}
impl<C> TryFrom<&[u8]> for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
type Error = Error;
fn try_from(input: &[u8]) -> Result<Self> {
Self::from_bytes(input)
}
}
impl<C> TryFrom<Signature<C>> for crate::Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
type Error = Error;
fn try_from(sig: Signature<C>) -> Result<super::Signature<C>> {
let mut bytes = super::SignatureBytes::<C>::default();
let r_begin = C::FieldBytesSize::USIZE.saturating_sub(sig.r().len());
let s_begin = bytes.len().saturating_sub(sig.s().len());
bytes[r_begin..C::FieldBytesSize::USIZE].copy_from_slice(sig.r());
bytes[s_begin..].copy_from_slice(sig.s());
Self::try_from(bytes.as_slice())
}
}
#[cfg(feature = "alloc")]
impl<C> From<Signature<C>> for Box<[u8]>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn from(signature: Signature<C>) -> Box<[u8]> {
signature.to_vec().into_boxed_slice()
}
}
#[cfg(feature = "alloc")]
impl<C> SignatureEncoding for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
type Repr = Box<[u8]>;
fn to_vec(&self) -> Vec<u8> {
self.as_bytes().into()
}
}
#[cfg(feature = "alloc")]
impl<C> SignatureBitStringEncoding for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn to_bitstring(&self) -> der::Result<BitString> {
BitString::new(0, self.to_vec())
}
}
#[cfg(feature = "serde")]
impl<C> Serialize for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serdect::slice::serialize_hex_upper_or_bin(&self.as_bytes(), serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, C> Deserialize<'de> for Signature<C>
where
C: PrimeCurve,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let mut buf = SignatureBytes::<C>::default();
let slice = serdect::slice::deserialize_hex_or_bin(&mut buf, deserializer)?;
Self::try_from(slice).map_err(de::Error::custom)
}
}
/// Decode the `r` and `s` components of a DER-encoded ECDSA signature.
fn decode_der(der_bytes: &[u8]) -> der::Result<(UintRef<'_>, UintRef<'_>)> {
let mut reader = der::SliceReader::new(der_bytes)?;
let header = der::Header::decode(&mut reader)?;
header.tag.assert_eq(der::Tag::Sequence)?;
let ret = reader.read_nested(header.length, |reader| {
let r = UintRef::decode(reader)?;
let s = UintRef::decode(reader)?;
Ok((r, s))
})?;
reader.finish(ret)
}
/// Locate the range within a slice at which a particular subslice is located
fn find_scalar_range(outer: &[u8], inner: &[u8]) -> Result<Range<usize>> {
let outer_start = outer.as_ptr() as usize;
let inner_start = inner.as_ptr() as usize;
let start = inner_start
.checked_sub(outer_start)
.ok_or_else(Error::new)?;
let end = start.checked_add(inner.len()).ok_or_else(Error::new)?;
Ok(Range { start, end })
}
#[cfg(all(feature = "digest", feature = "hazmat"))]
impl<C> signature::PrehashSignature for Signature<C>
where
C: PrimeCurve + crate::hazmat::DigestPrimitive,
MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<MaxOverhead> + ArrayLength<u8>,
{
type Digest = C::Digest;
}
#[cfg(all(test, feature = "arithmetic"))]
mod tests {
use elliptic_curve::dev::MockCurve;
type Signature = crate::Signature<MockCurve>;
const EXAMPLE_SIGNATURE: [u8; 64] = [
0xf3, 0xac, 0x80, 0x61, 0xb5, 0x14, 0x79, 0x5b, 0x88, 0x43, 0xe3, 0xd6, 0x62, 0x95, 0x27,
0xed, 0x2a, 0xfd, 0x6b, 0x1f, 0x6a, 0x55, 0x5a, 0x7a, 0xca, 0xbb, 0x5e, 0x6f, 0x79, 0xc8,
0xc2, 0xac, 0x8b, 0xf7, 0x78, 0x19, 0xca, 0x5, 0xa6, 0xb2, 0x78, 0x6c, 0x76, 0x26, 0x2b,
0xf7, 0x37, 0x1c, 0xef, 0x97, 0xb2, 0x18, 0xe9, 0x6f, 0x17, 0x5a, 0x3c, 0xcd, 0xda, 0x2a,
0xcc, 0x5, 0x89, 0x3,
];
#[test]
fn test_fixed_to_asn1_signature_roundtrip() {
let signature1 = Signature::try_from(EXAMPLE_SIGNATURE.as_ref()).unwrap();
// Convert to ASN.1 DER and back
let asn1_signature = signature1.to_der();
let signature2 = Signature::from_der(asn1_signature.as_ref()).unwrap();
assert_eq!(signature1, signature2);
}
#[test]
fn test_asn1_too_short_signature() {
assert!(Signature::from_der(&[]).is_err());
assert!(Signature::from_der(&[der::Tag::Sequence.into()]).is_err());
assert!(Signature::from_der(&[der::Tag::Sequence.into(), 0x00]).is_err());
assert!(Signature::from_der(&[
der::Tag::Sequence.into(),
0x03,
der::Tag::Integer.into(),
0x01,
0x01
])
.is_err());
}
#[test]
fn test_asn1_non_der_signature() {
// A minimal 8-byte ASN.1 signature parses OK.
assert!(Signature::from_der(&[
der::Tag::Sequence.into(),
0x06, // length of below
der::Tag::Integer.into(),
0x01, // length of value
0x01, // value=1
der::Tag::Integer.into(),
0x01, // length of value
0x01, // value=1
])
.is_ok());
// But length fields that are not minimally encoded should be rejected, as they are not
// valid DER, cf.
// https://github.com/google/wycheproof/blob/2196000605e4/testvectors/ecdsa_secp256k1_sha256_test.json#L57-L66
assert!(Signature::from_der(&[
der::Tag::Sequence.into(),
0x81, // extended length: 1 length byte to come
0x06, // length of below
der::Tag::Integer.into(),
0x01, // length of value
0x01, // value=1
der::Tag::Integer.into(),
0x01, // length of value
0x01, // value=1
])
.is_err());
}
}

230
vendor/ecdsa/src/dev.rs vendored Normal file
View File

@@ -0,0 +1,230 @@
//! Development-related functionality.
// TODO(tarcieri): implement full set of tests from ECDSA2VS
// <https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/dss2/ecdsa2vs.pdf>
/// ECDSA test vector
pub struct TestVector {
/// Private scalar
pub d: &'static [u8],
/// Public key x-coordinate (`Qx`)
pub q_x: &'static [u8],
/// Public key y-coordinate (`Qy`)
pub q_y: &'static [u8],
/// Ephemeral scalar (a.k.a. nonce)
pub k: &'static [u8],
/// Message digest (prehashed)
pub m: &'static [u8],
/// Signature `r` component
pub r: &'static [u8],
/// Signature `s` component
pub s: &'static [u8],
}
/// Define ECDSA signing test.
#[macro_export]
macro_rules! new_signing_test {
($curve:path, $vectors:expr) => {
use $crate::{
elliptic_curve::{
bigint::Encoding,
generic_array::{typenum::Unsigned, GenericArray},
group::ff::PrimeField,
Curve, CurveArithmetic, Scalar,
},
hazmat::SignPrimitive,
};
fn decode_scalar(bytes: &[u8]) -> Option<Scalar<$curve>> {
if bytes.len() == <$curve as Curve>::FieldBytesSize::USIZE {
Scalar::<$curve>::from_repr(GenericArray::clone_from_slice(bytes)).into()
} else {
None
}
}
#[test]
fn ecdsa_signing() {
for vector in $vectors {
let d = decode_scalar(vector.d).expect("invalid vector.d");
let k = decode_scalar(vector.k).expect("invalid vector.m");
assert_eq!(
<$curve as Curve>::FieldBytesSize::USIZE,
vector.m.len(),
"invalid vector.m (must be field-sized digest)"
);
let z = GenericArray::clone_from_slice(vector.m);
let sig = d.try_sign_prehashed(k, &z).expect("ECDSA sign failed").0;
assert_eq!(vector.r, sig.r().to_bytes().as_slice());
assert_eq!(vector.s, sig.s().to_bytes().as_slice());
}
}
};
}
/// Define ECDSA verification test.
#[macro_export]
macro_rules! new_verification_test {
($curve:path, $vectors:expr) => {
use $crate::{
elliptic_curve::{
generic_array::GenericArray,
group::ff::PrimeField,
sec1::{EncodedPoint, FromEncodedPoint},
AffinePoint, CurveArithmetic, Scalar,
},
hazmat::VerifyPrimitive,
Signature,
};
#[test]
fn ecdsa_verify_success() {
for vector in $vectors {
let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates(
GenericArray::from_slice(vector.q_x),
GenericArray::from_slice(vector.q_y),
false,
);
let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap();
let z = GenericArray::clone_from_slice(vector.m);
let sig = Signature::from_scalars(
GenericArray::clone_from_slice(vector.r),
GenericArray::clone_from_slice(vector.s),
)
.unwrap();
let result = q.verify_prehashed(&z, &sig);
assert!(result.is_ok());
}
}
#[test]
fn ecdsa_verify_invalid_s() {
for vector in $vectors {
let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates(
GenericArray::from_slice(vector.q_x),
GenericArray::from_slice(vector.q_y),
false,
);
let q = AffinePoint::<$curve>::from_encoded_point(&q_encoded).unwrap();
let z = GenericArray::clone_from_slice(vector.m);
// Flip a bit in `s`
let mut s_tweaked = GenericArray::clone_from_slice(vector.s);
s_tweaked[0] ^= 1;
let sig =
Signature::from_scalars(GenericArray::clone_from_slice(vector.r), s_tweaked)
.unwrap();
let result = q.verify_prehashed(&z, &sig);
assert!(result.is_err());
}
}
// TODO(tarcieri): test invalid Q, invalid r, invalid m
};
}
/// Define a Wycheproof verification test.
#[macro_export]
macro_rules! new_wycheproof_test {
($name:ident, $test_name: expr, $curve:path) => {
use $crate::{
elliptic_curve::{bigint::Integer, sec1::EncodedPoint},
signature::Verifier,
Signature,
};
#[test]
fn $name() {
use blobby::Blob5Iterator;
use elliptic_curve::{bigint::Encoding as _, generic_array::typenum::Unsigned};
// Build a field element but allow for too-short input (left pad with zeros)
// or too-long input (check excess leftmost bytes are zeros).
fn element_from_padded_slice<C: elliptic_curve::Curve>(
data: &[u8],
) -> elliptic_curve::FieldBytes<C> {
let point_len = C::FieldBytesSize::USIZE;
if data.len() >= point_len {
let offset = data.len() - point_len;
for v in data.iter().take(offset) {
assert_eq!(*v, 0, "EcdsaVerifier: point too large");
}
elliptic_curve::FieldBytes::<C>::clone_from_slice(&data[offset..])
} else {
// Provided slice is too short and needs to be padded with zeros
// on the left. Build a combined exact iterator to do this.
let iter = core::iter::repeat(0)
.take(point_len - data.len())
.chain(data.iter().cloned());
elliptic_curve::FieldBytes::<C>::from_exact_iter(iter).unwrap()
}
}
fn run_test(
wx: &[u8],
wy: &[u8],
msg: &[u8],
sig: &[u8],
pass: bool,
) -> Option<&'static str> {
let x = element_from_padded_slice::<$curve>(wx);
let y = element_from_padded_slice::<$curve>(wy);
let q_encoded = EncodedPoint::<$curve>::from_affine_coordinates(
&x, &y, /* compress= */ false,
);
let verifying_key =
$crate::VerifyingKey::<$curve>::from_encoded_point(&q_encoded).unwrap();
let sig = match Signature::from_der(sig) {
Ok(s) => s,
Err(_) if !pass => return None,
Err(_) => return Some("failed to parse signature ASN.1"),
};
match verifying_key.verify(msg, &sig) {
Ok(_) if pass => None,
Ok(_) => Some("signature verify unexpectedly succeeded"),
Err(_) if !pass => None,
Err(_) => Some("signature verify failed"),
}
}
let data = include_bytes!(concat!("test_vectors/data/", $test_name, ".blb"));
for (i, row) in Blob5Iterator::new(data).unwrap().enumerate() {
let [wx, wy, msg, sig, status] = row.unwrap();
let pass = match status[0] {
0 => false,
1 => true,
_ => panic!("invalid value for pass flag"),
};
if let Some(desc) = run_test(wx, wy, msg, sig, pass) {
panic!(
"\n\
Failed test №{}: {}\n\
wx:\t{:?}\n\
wy:\t{:?}\n\
msg:\t{:?}\n\
sig:\t{:?}\n\
pass:\t{}\n",
i, desc, wx, wy, msg, sig, pass,
);
}
}
}
};
}

332
vendor/ecdsa/src/hazmat.rs vendored Normal file
View File

@@ -0,0 +1,332 @@
//! Low-level ECDSA primitives.
//!
//! # ⚠️ Warning: Hazmat!
//!
//! YOU PROBABLY DON'T WANT TO USE THESE!
//!
//! These primitives are easy-to-misuse low-level interfaces.
//!
//! If you are an end user / non-expert in cryptography, do not use these!
//! Failure to use them correctly can lead to catastrophic failures including
//! FULL PRIVATE KEY RECOVERY!
use crate::{Error, Result};
use core::cmp;
use elliptic_curve::{generic_array::typenum::Unsigned, FieldBytes, PrimeCurve};
#[cfg(feature = "arithmetic")]
use {
crate::{RecoveryId, SignatureSize},
elliptic_curve::{
ff::{Field, PrimeField},
group::{Curve as _, Group},
ops::{Invert, LinearCombination, MulByGenerator, Reduce},
point::AffineCoordinates,
scalar::IsHigh,
subtle::CtOption,
CurveArithmetic, ProjectivePoint, Scalar,
},
};
#[cfg(feature = "digest")]
use {
elliptic_curve::FieldBytesSize,
signature::{
digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset},
PrehashSignature,
},
};
#[cfg(feature = "rfc6979")]
use elliptic_curve::{FieldBytesEncoding, ScalarPrimitive};
#[cfg(any(feature = "arithmetic", feature = "digest"))]
use crate::{elliptic_curve::generic_array::ArrayLength, Signature};
/// Try to sign the given prehashed message using ECDSA.
///
/// This trait is intended to be implemented on a type with access to the
/// secret scalar via `&self`, such as particular curve's `Scalar` type.
#[cfg(feature = "arithmetic")]
pub trait SignPrimitive<C>:
AsRef<Self>
+ Into<FieldBytes<C>>
+ IsHigh
+ PrimeField<Repr = FieldBytes<C>>
+ Reduce<C::Uint, Bytes = FieldBytes<C>>
+ Sized
where
C: PrimeCurve + CurveArithmetic<Scalar = Self>,
SignatureSize<C>: ArrayLength<u8>,
{
/// Try to sign the prehashed message.
///
/// Accepts the following arguments:
///
/// - `k`: ephemeral scalar value. MUST BE UNIFORMLY RANDOM!!!
/// - `z`: message digest to be signed. MUST BE OUTPUT OF A CRYPTOGRAPHICALLY
/// SECURE DIGEST ALGORITHM!!!
///
/// # Returns
///
/// ECDSA [`Signature`] and, when possible/desired, a [`RecoveryId`]
/// which can be used to recover the verifying key for a given signature.
fn try_sign_prehashed<K>(
&self,
k: K,
z: &FieldBytes<C>,
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
K: AsRef<Self> + Invert<Output = CtOption<Self>>,
{
sign_prehashed(self, k, z).map(|(sig, recid)| (sig, (Some(recid))))
}
/// Try to sign the given message digest deterministically using the method
/// described in [RFC6979] for computing ECDSA ephemeral scalar `k`.
///
/// Accepts the following parameters:
/// - `z`: message digest to be signed.
/// - `ad`: optional additional data, e.g. added entropy from an RNG
///
/// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979
#[cfg(feature = "rfc6979")]
fn try_sign_prehashed_rfc6979<D>(
&self,
z: &FieldBytes<C>,
ad: &[u8],
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
Self: From<ScalarPrimitive<C>> + Invert<Output = CtOption<Self>>,
D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldBytesSize<C>> + FixedOutputReset,
{
let k = Scalar::<C>::from_repr(rfc6979::generate_k::<D, _>(
&self.to_repr(),
&C::ORDER.encode_field_bytes(),
z,
ad,
))
.unwrap();
self.try_sign_prehashed::<Self>(k, z)
}
}
/// Verify the given prehashed message using ECDSA.
///
/// This trait is intended to be implemented on type which can access
/// the affine point represeting the public key via `&self`, such as a
/// particular curve's `AffinePoint` type.
#[cfg(feature = "arithmetic")]
pub trait VerifyPrimitive<C>: AffineCoordinates<FieldRepr = FieldBytes<C>> + Copy + Sized
where
C: PrimeCurve + CurveArithmetic<AffinePoint = Self>,
SignatureSize<C>: ArrayLength<u8>,
{
/// Verify the prehashed message against the provided ECDSA signature.
///
/// Accepts the following arguments:
///
/// - `z`: message digest to be verified. MUST BE OUTPUT OF A
/// CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!!
/// - `sig`: signature to be verified against the key and message
fn verify_prehashed(&self, z: &FieldBytes<C>, sig: &Signature<C>) -> Result<()> {
verify_prehashed(&ProjectivePoint::<C>::from(*self), z, sig)
}
/// Verify message digest against the provided signature.
#[cfg(feature = "digest")]
fn verify_digest<D>(&self, msg_digest: D, sig: &Signature<C>) -> Result<()>
where
D: FixedOutput<OutputSize = FieldBytesSize<C>>,
{
self.verify_prehashed(&msg_digest.finalize_fixed(), sig)
}
}
/// Bind a preferred [`Digest`] algorithm to an elliptic curve type.
///
/// Generally there is a preferred variety of the SHA-2 family used with ECDSA
/// for a particular elliptic curve.
///
/// This trait can be used to specify it, and with it receive a blanket impl of
/// [`PrehashSignature`], used by [`signature_derive`][1]) for the [`Signature`]
/// type for a particular elliptic curve.
///
/// [1]: https://github.com/RustCrypto/traits/tree/master/signature/derive
#[cfg(feature = "digest")]
pub trait DigestPrimitive: PrimeCurve {
/// Preferred digest to use when computing ECDSA signatures for this
/// elliptic curve. This is typically a member of the SHA-2 family.
type Digest: BlockSizeUser
+ Digest
+ FixedOutput<OutputSize = FieldBytesSize<Self>>
+ FixedOutputReset;
}
#[cfg(feature = "digest")]
impl<C> PrehashSignature for Signature<C>
where
C: DigestPrimitive,
<FieldBytesSize<C> as core::ops::Add>::Output: ArrayLength<u8>,
{
type Digest = C::Digest;
}
/// Partial implementation of the `bits2int` function as defined in
/// [RFC6979 § 2.3.2] as well as [SEC1] § 2.3.8.
///
/// This is used to convert a message digest whose size may be smaller or
/// larger than the size of the curve's scalar field into a serialized
/// (unreduced) field element.
///
/// [RFC6979 § 2.3.2]: https://datatracker.ietf.org/doc/html/rfc6979#section-2.3.2
/// [SEC1]: https://www.secg.org/sec1-v2.pdf
pub fn bits2field<C: PrimeCurve>(bits: &[u8]) -> Result<FieldBytes<C>> {
// Minimum allowed bits size is half the field size
if bits.len() < C::FieldBytesSize::USIZE / 2 {
return Err(Error::new());
}
let mut field_bytes = FieldBytes::<C>::default();
match bits.len().cmp(&C::FieldBytesSize::USIZE) {
cmp::Ordering::Equal => field_bytes.copy_from_slice(bits),
cmp::Ordering::Less => {
// If bits is smaller than the field size, pad with zeroes on the left
field_bytes[(C::FieldBytesSize::USIZE - bits.len())..].copy_from_slice(bits);
}
cmp::Ordering::Greater => {
// If bits is larger than the field size, truncate
field_bytes.copy_from_slice(&bits[..C::FieldBytesSize::USIZE]);
}
}
Ok(field_bytes)
}
/// Sign a prehashed message digest using the provided secret scalar and
/// ephemeral scalar, returning an ECDSA signature.
///
/// Accepts the following arguments:
///
/// - `d`: signing key. MUST BE UNIFORMLY RANDOM!!!
/// - `k`: ephemeral scalar value. MUST BE UNIFORMLY RANDOM!!!
/// - `z`: message digest to be signed. MUST BE OUTPUT OF A CRYPTOGRAPHICALLY
/// SECURE DIGEST ALGORITHM!!!
///
/// # Returns
///
/// ECDSA [`Signature`] and, when possible/desired, a [`RecoveryId`]
/// which can be used to recover the verifying key for a given signature.
#[cfg(feature = "arithmetic")]
#[allow(non_snake_case)]
pub fn sign_prehashed<C, K>(
d: &Scalar<C>,
k: K,
z: &FieldBytes<C>,
) -> Result<(Signature<C>, RecoveryId)>
where
C: PrimeCurve + CurveArithmetic,
K: AsRef<Scalar<C>> + Invert<Output = CtOption<Scalar<C>>>,
SignatureSize<C>: ArrayLength<u8>,
{
// TODO(tarcieri): use `NonZeroScalar<C>` for `k`.
if k.as_ref().is_zero().into() {
return Err(Error::new());
}
let z = <Scalar<C> as Reduce<C::Uint>>::reduce_bytes(z);
// Compute scalar inversion of 𝑘
let k_inv = Option::<Scalar<C>>::from(k.invert()).ok_or_else(Error::new)?;
// Compute 𝑹 = 𝑘×𝑮
let R = ProjectivePoint::<C>::mul_by_generator(k.as_ref()).to_affine();
// Lift x-coordinate of 𝑹 (element of base field) into a serialized big
// integer, then reduce it into an element of the scalar field
let r = Scalar::<C>::reduce_bytes(&R.x());
let x_is_reduced = r.to_repr() != R.x();
// Compute 𝒔 as a signature over 𝒓 and 𝒛.
let s = k_inv * (z + (r * d));
// NOTE: `Signature::from_scalars` checks that both `r` and `s` are non-zero.
let signature = Signature::from_scalars(r, s)?;
let recovery_id = RecoveryId::new(R.y_is_odd().into(), x_is_reduced);
Ok((signature, recovery_id))
}
/// Verify the prehashed message against the provided ECDSA signature.
///
/// Accepts the following arguments:
///
/// - `q`: public key with which to verify the signature.
/// - `z`: message digest to be verified. MUST BE OUTPUT OF A
/// CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!!
/// - `sig`: signature to be verified against the key and message.
#[cfg(feature = "arithmetic")]
pub fn verify_prehashed<C>(
q: &ProjectivePoint<C>,
z: &FieldBytes<C>,
sig: &Signature<C>,
) -> Result<()>
where
C: PrimeCurve + CurveArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
let z = Scalar::<C>::reduce_bytes(z);
let (r, s) = sig.split_scalars();
let s_inv = *s.invert_vartime();
let u1 = z * s_inv;
let u2 = *r * s_inv;
let x = ProjectivePoint::<C>::lincomb(&ProjectivePoint::<C>::generator(), &u1, q, &u2)
.to_affine()
.x();
if *r == Scalar::<C>::reduce_bytes(&x) {
Ok(())
} else {
Err(Error::new())
}
}
#[cfg(test)]
mod tests {
use super::bits2field;
use elliptic_curve::dev::MockCurve;
use hex_literal::hex;
#[test]
fn bits2field_too_small() {
assert!(bits2field::<MockCurve>(b"").is_err());
}
#[test]
fn bits2field_size_less() {
let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
assert_eq!(
field_bytes.as_slice(),
&hex!("00000000000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
);
}
#[test]
fn bits2field_size_eq() {
let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
assert_eq!(field_bytes.as_slice(), &prehash);
}
#[test]
fn bits2field_size_greater() {
let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
assert_eq!(
field_bytes.as_slice(),
&hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
);
}
}

709
vendor/ecdsa/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,709 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/8f1a9894/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(
clippy::cast_lossless,
clippy::cast_possible_truncation,
clippy::cast_possible_wrap,
clippy::cast_precision_loss,
clippy::cast_sign_loss,
clippy::checked_conversions,
clippy::implicit_saturating_sub,
clippy::panic,
clippy::panic_in_result_fn,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_lifetimes,
unused_qualifications
)]
//! ## `serde` support
//!
//! When the `serde` feature of this crate is enabled, `Serialize` and
//! `Deserialize` impls are provided for the [`Signature`] and [`VerifyingKey`]
//! types.
//!
//! Please see type-specific documentation for more information.
//!
//! ## Interop
//!
//! Any crates which provide an implementation of ECDSA for a particular
//! elliptic curve can leverage the types from this crate, along with the
//! [`k256`], [`p256`], and/or [`p384`] crates to expose ECDSA functionality in
//! a generic, interoperable way by leveraging the [`Signature`] type with in
//! conjunction with the [`signature::Signer`] and [`signature::Verifier`]
//! traits.
//!
//! For example, the [`ring-compat`] crate implements the [`signature::Signer`]
//! and [`signature::Verifier`] traits in conjunction with the
//! [`p256::ecdsa::Signature`] and [`p384::ecdsa::Signature`] types to
//! wrap the ECDSA implementations from [*ring*] in a generic, interoperable
//! API.
//!
//! [`k256`]: https://docs.rs/k256
//! [`p256`]: https://docs.rs/p256
//! [`p256::ecdsa::Signature`]: https://docs.rs/p256/latest/p256/ecdsa/type.Signature.html
//! [`p384`]: https://docs.rs/p384
//! [`p384::ecdsa::Signature`]: https://docs.rs/p384/latest/p384/ecdsa/type.Signature.html
//! [`ring-compat`]: https://docs.rs/ring-compat
//! [*ring*]: https://docs.rs/ring
#[cfg(feature = "alloc")]
extern crate alloc;
mod normalized;
mod recovery;
#[cfg(feature = "der")]
pub mod der;
#[cfg(feature = "dev")]
pub mod dev;
#[cfg(feature = "hazmat")]
pub mod hazmat;
#[cfg(feature = "signing")]
mod signing;
#[cfg(feature = "verifying")]
mod verifying;
pub use crate::{normalized::NormalizedSignature, recovery::RecoveryId};
// Re-export the `elliptic-curve` crate (and select types)
pub use elliptic_curve::{self, sec1::EncodedPoint, PrimeCurve};
// Re-export the `signature` crate (and select types)
pub use signature::{self, Error, Result, SignatureEncoding};
#[cfg(feature = "signing")]
pub use crate::signing::SigningKey;
#[cfg(feature = "verifying")]
pub use crate::verifying::VerifyingKey;
use core::{fmt, ops::Add};
use elliptic_curve::{
generic_array::{typenum::Unsigned, ArrayLength, GenericArray},
FieldBytes, FieldBytesSize, ScalarPrimitive,
};
#[cfg(feature = "alloc")]
use alloc::vec::Vec;
#[cfg(feature = "arithmetic")]
use {
core::str,
elliptic_curve::{scalar::IsHigh, CurveArithmetic, NonZeroScalar},
};
#[cfg(feature = "digest")]
use digest::{
const_oid::{AssociatedOid, ObjectIdentifier},
Digest,
};
#[cfg(feature = "pkcs8")]
use elliptic_curve::pkcs8::spki::{
der::AnyRef, AlgorithmIdentifierRef, AssociatedAlgorithmIdentifier,
};
#[cfg(feature = "serde")]
use serdect::serde::{de, ser, Deserialize, Serialize};
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
use elliptic_curve::pkcs8::spki::{
self, AlgorithmIdentifierOwned, DynAssociatedAlgorithmIdentifier,
};
/// OID for ECDSA with SHA-224 digests.
///
/// ```text
/// ecdsa-with-SHA224 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
/// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 1 }
/// ```
// TODO(tarcieri): use `ObjectIdentifier::push_arc` when const unwrap is stable
#[cfg(feature = "digest")]
pub const ECDSA_SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.1");
/// OID for ECDSA with SHA-256 digests.
///
/// ```text
/// ecdsa-with-SHA256 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
/// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 2 }
/// ```
#[cfg(feature = "digest")]
pub const ECDSA_SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.2");
/// OID for ECDSA with SHA-384 digests.
///
/// ```text
/// ecdsa-with-SHA384 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
/// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 3 }
/// ```
#[cfg(feature = "digest")]
pub const ECDSA_SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.3");
/// OID for ECDSA with SHA-512 digests.
///
/// ```text
/// ecdsa-with-SHA512 OBJECT IDENTIFIER ::= { iso(1) member-body(2)
/// us(840) ansi-X9-62(10045) signatures(4) ecdsa-with-SHA2(3) 4 }
/// ```
#[cfg(feature = "digest")]
pub const ECDSA_SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.4.3.4");
#[cfg(feature = "digest")]
const SHA224_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.4");
#[cfg(feature = "digest")]
const SHA256_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1");
#[cfg(feature = "digest")]
const SHA384_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.2");
#[cfg(feature = "digest")]
const SHA512_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.3");
/// Size of a fixed sized signature for the given elliptic curve.
pub type SignatureSize<C> = <FieldBytesSize<C> as Add>::Output;
/// Fixed-size byte array containing an ECDSA signature
pub type SignatureBytes<C> = GenericArray<u8, SignatureSize<C>>;
/// ECDSA signature (fixed-size). Generic over elliptic curve types.
///
/// Serialized as fixed-sized big endian scalar values with no added framing:
///
/// - `r`: field element size for the given curve, big-endian
/// - `s`: field element size for the given curve, big-endian
///
/// Both `r` and `s` MUST be non-zero.
///
/// For example, in a curve with a 256-bit modulus like NIST P-256 or
/// secp256k1, `r` and `s` will both be 32-bytes and serialized as big endian,
/// resulting in a signature with a total of 64-bytes.
///
/// ASN.1 DER-encoded signatures also supported via the
/// [`Signature::from_der`] and [`Signature::to_der`] methods.
///
/// # `serde` support
///
/// When the `serde` feature of this crate is enabled, it provides support for
/// serializing and deserializing ECDSA signatures using the `Serialize` and
/// `Deserialize` traits.
///
/// The serialization uses a hexadecimal encoding when used with
/// "human readable" text formats, and a binary encoding otherwise.
#[derive(Clone, Eq, PartialEq)]
pub struct Signature<C: PrimeCurve> {
r: ScalarPrimitive<C>,
s: ScalarPrimitive<C>,
}
impl<C> Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
/// Parse a signature from fixed-width bytes, i.e. 2 * the size of
/// [`FieldBytes`] for a particular curve.
///
/// # Returns
/// - `Ok(signature)` if the `r` and `s` components are both in the valid
/// range `1..n` when serialized as concatenated big endian integers.
/// - `Err(err)` if the `r` and/or `s` component of the signature is
/// out-of-range when interpreted as a big endian integer.
pub fn from_bytes(bytes: &SignatureBytes<C>) -> Result<Self> {
let (r_bytes, s_bytes) = bytes.split_at(C::FieldBytesSize::USIZE);
let r = FieldBytes::<C>::clone_from_slice(r_bytes);
let s = FieldBytes::<C>::clone_from_slice(s_bytes);
Self::from_scalars(r, s)
}
/// Parse a signature from a byte slice.
pub fn from_slice(slice: &[u8]) -> Result<Self> {
if slice.len() == SignatureSize::<C>::USIZE {
Self::from_bytes(SignatureBytes::<C>::from_slice(slice))
} else {
Err(Error::new())
}
}
/// Parse a signature from ASN.1 DER.
#[cfg(feature = "der")]
pub fn from_der(bytes: &[u8]) -> Result<Self>
where
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
der::Signature::<C>::try_from(bytes).and_then(Self::try_from)
}
/// Create a [`Signature`] from the serialized `r` and `s` scalar values
/// which comprise the signature.
///
/// # Returns
/// - `Ok(signature)` if the `r` and `s` components are both in the valid
/// range `1..n` when serialized as concatenated big endian integers.
/// - `Err(err)` if the `r` and/or `s` component of the signature is
/// out-of-range when interpreted as a big endian integer.
pub fn from_scalars(r: impl Into<FieldBytes<C>>, s: impl Into<FieldBytes<C>>) -> Result<Self> {
let r = ScalarPrimitive::from_slice(&r.into()).map_err(|_| Error::new())?;
let s = ScalarPrimitive::from_slice(&s.into()).map_err(|_| Error::new())?;
if r.is_zero().into() || s.is_zero().into() {
return Err(Error::new());
}
Ok(Self { r, s })
}
/// Split the signature into its `r` and `s` components, represented as bytes.
pub fn split_bytes(&self) -> (FieldBytes<C>, FieldBytes<C>) {
(self.r.to_bytes(), self.s.to_bytes())
}
/// Serialize this signature as bytes.
pub fn to_bytes(&self) -> SignatureBytes<C> {
let mut bytes = SignatureBytes::<C>::default();
let (r_bytes, s_bytes) = bytes.split_at_mut(C::FieldBytesSize::USIZE);
r_bytes.copy_from_slice(&self.r.to_bytes());
s_bytes.copy_from_slice(&self.s.to_bytes());
bytes
}
/// Serialize this signature as ASN.1 DER.
#[cfg(feature = "der")]
pub fn to_der(&self) -> der::Signature<C>
where
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
let (r, s) = self.split_bytes();
der::Signature::from_components(&r, &s).expect("DER encoding error")
}
/// Convert this signature into a byte vector.
#[cfg(feature = "alloc")]
pub fn to_vec(&self) -> Vec<u8> {
self.to_bytes().to_vec()
}
}
#[cfg(feature = "arithmetic")]
impl<C> Signature<C>
where
C: PrimeCurve + CurveArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
/// Get the `r` component of this signature
pub fn r(&self) -> NonZeroScalar<C> {
NonZeroScalar::new(self.r.into()).unwrap()
}
/// Get the `s` component of this signature
pub fn s(&self) -> NonZeroScalar<C> {
NonZeroScalar::new(self.s.into()).unwrap()
}
/// Split the signature into its `r` and `s` scalars.
pub fn split_scalars(&self) -> (NonZeroScalar<C>, NonZeroScalar<C>) {
(self.r(), self.s())
}
/// Normalize signature into "low S" form as described in
/// [BIP 0062: Dealing with Malleability][1].
///
/// [1]: https://github.com/bitcoin/bips/blob/master/bip-0062.mediawiki
pub fn normalize_s(&self) -> Option<Self> {
let s = self.s();
if s.is_high().into() {
let mut result = self.clone();
result.s = ScalarPrimitive::from(-s);
Some(result)
} else {
None
}
}
}
impl<C> Copy for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
<SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
{
}
impl<C> From<Signature<C>> for SignatureBytes<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(signature: Signature<C>) -> SignatureBytes<C> {
signature.to_bytes()
}
}
impl<C> SignatureEncoding for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
type Repr = SignatureBytes<C>;
}
impl<C> TryFrom<&[u8]> for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
type Error = Error;
fn try_from(slice: &[u8]) -> Result<Self> {
Self::from_slice(slice)
}
}
impl<C> fmt::Debug for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "ecdsa::Signature<{:?}>(", C::default())?;
for byte in self.to_bytes() {
write!(f, "{:02X}", byte)?;
}
write!(f, ")")
}
}
impl<C> fmt::Display for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:X}", self)
}
}
impl<C> fmt::LowerHex for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.to_bytes() {
write!(f, "{:02x}", byte)?;
}
Ok(())
}
}
impl<C> fmt::UpperHex for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.to_bytes() {
write!(f, "{:02X}", byte)?;
}
Ok(())
}
}
#[cfg(feature = "arithmetic")]
impl<C> str::FromStr for Signature<C>
where
C: PrimeCurve + CurveArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
type Err = Error;
fn from_str(hex: &str) -> Result<Self> {
if hex.as_bytes().len() != C::FieldBytesSize::USIZE * 4 {
return Err(Error::new());
}
// This check is mainly to ensure `hex.split_at` below won't panic
if !hex
.as_bytes()
.iter()
.all(|&byte| matches!(byte, b'0'..=b'9' | b'a'..=b'z' | b'A'..=b'Z'))
{
return Err(Error::new());
}
let (r_hex, s_hex) = hex.split_at(C::FieldBytesSize::USIZE * 2);
let r = r_hex
.parse::<NonZeroScalar<C>>()
.map_err(|_| Error::new())?;
let s = s_hex
.parse::<NonZeroScalar<C>>()
.map_err(|_| Error::new())?;
Self::from_scalars(r, s)
}
}
/// ECDSA [`ObjectIdentifier`] which identifies the digest used by default
/// with the `Signer` and `Verifier` traits.
///
/// To support non-default digest algorithms, use the [`SignatureWithOid`]
/// type instead.
#[cfg(all(feature = "digest", feature = "hazmat"))]
impl<C> AssociatedOid for Signature<C>
where
C: hazmat::DigestPrimitive,
C::Digest: AssociatedOid,
{
const OID: ObjectIdentifier = match ecdsa_oid_for_digest(C::Digest::OID) {
Some(oid) => oid,
None => panic!("no RFC5758 ECDSA OID defined for DigestPrimitive::Digest"),
};
}
/// ECDSA `AlgorithmIdentifier` which identifies the digest used by default
/// with the `Signer` and `Verifier` traits.
#[cfg(feature = "pkcs8")]
impl<C> AssociatedAlgorithmIdentifier for Signature<C>
where
C: PrimeCurve,
Self: AssociatedOid,
{
type Params = AnyRef<'static>;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifierRef<'static> = AlgorithmIdentifierRef {
oid: Self::OID,
parameters: None,
};
}
#[cfg(feature = "serde")]
impl<C> Serialize for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
serdect::array::serialize_hex_upper_or_bin(&self.to_bytes(), serializer)
}
}
#[cfg(feature = "serde")]
impl<'de, C> Deserialize<'de> for Signature<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let mut bytes = SignatureBytes::<C>::default();
serdect::array::deserialize_hex_or_bin(&mut bytes, deserializer)?;
Self::try_from(bytes.as_slice()).map_err(de::Error::custom)
}
}
/// An extended [`Signature`] type which is parameterized by an
/// `ObjectIdentifier` which identifies the ECDSA variant used by a
/// particular signature.
///
/// Valid `ObjectIdentifiers` are defined in [RFC5758 § 3.2]:
///
/// - SHA-224: [`ECDSA_SHA224_OID`] (1.2.840.10045.4.3.1)
/// - SHA-256: [`ECDSA_SHA256_OID`] (1.2.840.10045.4.3.2)
/// - SHA-384: [`ECDSA_SHA384_OID`] (1.2.840.10045.4.3.3)
/// - SHA-512: [`ECDSA_SHA512_OID`] (1.2.840.10045.4.3.4)
///
/// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2
#[cfg(feature = "digest")]
#[derive(Clone, Eq, PartialEq)]
pub struct SignatureWithOid<C: PrimeCurve> {
/// Inner signature type.
signature: Signature<C>,
/// OID which identifies the ECDSA variant used.
///
/// MUST be one of the ECDSA algorithm variants as defined in RFC5758.
///
/// These OIDs begin with `1.2.840.10045.4`.
oid: ObjectIdentifier,
}
#[cfg(feature = "digest")]
impl<C> SignatureWithOid<C>
where
C: PrimeCurve,
{
/// Create a new signature with an explicitly provided OID.
///
/// OID must begin with `1.2.840.10045.4`, the [RFC5758] OID prefix for
/// ECDSA variants.
///
/// [RFC5758]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2
pub fn new(signature: Signature<C>, oid: ObjectIdentifier) -> Result<Self> {
// TODO(tarcieri): use `ObjectIdentifier::starts_with`
for (arc1, arc2) in ObjectIdentifier::new_unwrap("1.2.840.10045.4.3")
.arcs()
.zip(oid.arcs())
{
if arc1 != arc2 {
return Err(Error::new());
}
}
Ok(Self { signature, oid })
}
/// Create a new signature, determining the OID from the given digest.
///
/// Supports SHA-2 family digests as enumerated in [RFC5758 § 3.2], i.e.
/// SHA-224, SHA-256, SHA-384, or SHA-512.
///
/// [RFC5758 § 3.2]: https://www.rfc-editor.org/rfc/rfc5758#section-3.2
pub fn new_with_digest<D>(signature: Signature<C>) -> Result<Self>
where
D: AssociatedOid + Digest,
{
let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
Ok(Self { signature, oid })
}
/// Parse a signature from fixed-with bytes.
pub fn from_bytes_with_digest<D>(bytes: &SignatureBytes<C>) -> Result<Self>
where
D: AssociatedOid + Digest,
SignatureSize<C>: ArrayLength<u8>,
{
Self::new_with_digest::<D>(Signature::<C>::from_bytes(bytes)?)
}
/// Parse a signature from a byte slice.
pub fn from_slice_with_digest<D>(slice: &[u8]) -> Result<Self>
where
D: AssociatedOid + Digest,
SignatureSize<C>: ArrayLength<u8>,
{
Self::new_with_digest::<D>(Signature::<C>::from_slice(slice)?)
}
/// Get the fixed-width ECDSA signature.
pub fn signature(&self) -> &Signature<C> {
&self.signature
}
/// Get the ECDSA OID for this signature.
pub fn oid(&self) -> ObjectIdentifier {
self.oid
}
/// Serialize this signature as bytes.
pub fn to_bytes(&self) -> SignatureBytes<C>
where
SignatureSize<C>: ArrayLength<u8>,
{
self.signature.to_bytes()
}
}
#[cfg(feature = "digest")]
impl<C> Copy for SignatureWithOid<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
<SignatureSize<C> as ArrayLength<u8>>::ArrayType: Copy,
{
}
#[cfg(feature = "digest")]
impl<C> From<SignatureWithOid<C>> for Signature<C>
where
C: PrimeCurve,
{
fn from(sig: SignatureWithOid<C>) -> Signature<C> {
sig.signature
}
}
#[cfg(feature = "digest")]
impl<C> From<SignatureWithOid<C>> for SignatureBytes<C>
where
C: PrimeCurve,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(signature: SignatureWithOid<C>) -> SignatureBytes<C> {
signature.to_bytes()
}
}
/// NOTE: this implementation assumes the default digest for the given elliptic
/// curve as defined by [`hazmat::DigestPrimitive`].
///
/// When working with alternative digests, you will need to use e.g.
/// [`SignatureWithOid::new_with_digest`].
#[cfg(all(feature = "digest", feature = "hazmat"))]
impl<C> SignatureEncoding for SignatureWithOid<C>
where
C: hazmat::DigestPrimitive,
C::Digest: AssociatedOid,
SignatureSize<C>: ArrayLength<u8>,
{
type Repr = SignatureBytes<C>;
}
/// NOTE: this implementation assumes the default digest for the given elliptic
/// curve as defined by [`hazmat::DigestPrimitive`].
///
/// When working with alternative digests, you will need to use e.g.
/// [`SignatureWithOid::new_with_digest`].
#[cfg(all(feature = "digest", feature = "hazmat"))]
impl<C> TryFrom<&[u8]> for SignatureWithOid<C>
where
C: hazmat::DigestPrimitive,
C::Digest: AssociatedOid,
SignatureSize<C>: ArrayLength<u8>,
{
type Error = Error;
fn try_from(slice: &[u8]) -> Result<Self> {
Self::new(Signature::<C>::from_slice(slice)?, C::Digest::OID)
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl<C> DynAssociatedAlgorithmIdentifier for SignatureWithOid<C>
where
C: PrimeCurve,
{
fn algorithm_identifier(&self) -> spki::Result<AlgorithmIdentifierOwned> {
Ok(AlgorithmIdentifierOwned {
oid: self.oid,
parameters: None,
})
}
}
/// Get the ECDSA OID for a given digest OID.
#[cfg(feature = "digest")]
const fn ecdsa_oid_for_digest(digest_oid: ObjectIdentifier) -> Option<ObjectIdentifier> {
match digest_oid {
SHA224_OID => Some(ECDSA_SHA224_OID),
SHA256_OID => Some(ECDSA_SHA256_OID),
SHA384_OID => Some(ECDSA_SHA384_OID),
SHA512_OID => Some(ECDSA_SHA512_OID),
_ => None,
}
}

11
vendor/ecdsa/src/normalized.rs vendored Normal file
View File

@@ -0,0 +1,11 @@
//! Support for ECDSA signatures with low-S normalization.
use crate::Signature;
use elliptic_curve::PrimeCurve;
/// ECDSA signature with low-S normalization applied.
#[derive(Clone, Eq, PartialEq)]
#[repr(transparent)]
pub struct NormalizedSignature<C: PrimeCurve> {
inner: Signature<C>,
}

357
vendor/ecdsa/src/recovery.rs vendored Normal file
View File

@@ -0,0 +1,357 @@
//! Public key recovery support.
use crate::{Error, Result};
#[cfg(feature = "signing")]
use {
crate::{hazmat::SignPrimitive, SigningKey},
elliptic_curve::subtle::CtOption,
signature::{hazmat::PrehashSigner, DigestSigner, Signer},
};
#[cfg(feature = "verifying")]
use {
crate::{hazmat::VerifyPrimitive, VerifyingKey},
elliptic_curve::{
bigint::CheckedAdd,
ops::{LinearCombination, Reduce},
point::DecompressPoint,
sec1::{self, FromEncodedPoint, ToEncodedPoint},
AffinePoint, FieldBytesEncoding, FieldBytesSize, Group, PrimeField, ProjectivePoint,
},
signature::hazmat::PrehashVerifier,
};
#[cfg(any(feature = "signing", feature = "verifying"))]
use {
crate::{
hazmat::{bits2field, DigestPrimitive},
Signature, SignatureSize,
},
elliptic_curve::{
generic_array::ArrayLength, ops::Invert, CurveArithmetic, PrimeCurve, Scalar,
},
signature::digest::Digest,
};
/// Recovery IDs, a.k.a. "recid".
///
/// This is an integer value `0`, `1`, `2`, or `3` included along with a
/// signature which is used during the recovery process to select the correct
/// public key from the signature.
///
/// It consists of two bits of information:
///
/// - low bit (0/1): was the y-coordinate of the affine point resulting from
/// the fixed-base multiplication 𝑘×𝑮 odd? This part of the algorithm
/// functions similar to point decompression.
/// - hi bit (3/4): did the affine x-coordinate of 𝑘×𝑮 overflow the order of
/// the scalar field, requiring a reduction when computing `r`?
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
pub struct RecoveryId(u8);
impl RecoveryId {
/// Maximum supported value for the recovery ID (inclusive).
pub const MAX: u8 = 3;
/// Create a new [`RecoveryId`] from the following 1-bit arguments:
///
/// - `is_y_odd`: is the affine y-coordinate of 𝑘×𝑮 odd?
/// - `is_x_reduced`: did the affine x-coordinate of 𝑘×𝑮 overflow the curve order?
pub const fn new(is_y_odd: bool, is_x_reduced: bool) -> Self {
Self((is_x_reduced as u8) << 1 | (is_y_odd as u8))
}
/// Did the affine x-coordinate of 𝑘×𝑮 overflow the curve order?
pub const fn is_x_reduced(self) -> bool {
(self.0 & 0b10) != 0
}
/// Is the affine y-coordinate of 𝑘×𝑮 odd?
pub const fn is_y_odd(self) -> bool {
(self.0 & 1) != 0
}
/// Convert a `u8` into a [`RecoveryId`].
pub const fn from_byte(byte: u8) -> Option<Self> {
if byte <= Self::MAX {
Some(Self(byte))
} else {
None
}
}
/// Convert this [`RecoveryId`] into a `u8`.
pub const fn to_byte(self) -> u8 {
self.0
}
}
#[cfg(feature = "verifying")]
impl RecoveryId {
/// Given a public key, message, and signature, use trial recovery
/// to determine if a suitable recovery ID exists, or return an error
/// otherwise.
pub fn trial_recovery_from_msg<C>(
verifying_key: &VerifyingKey<C>,
msg: &[u8],
signature: &Signature<C>,
) -> Result<Self>
where
C: DigestPrimitive + PrimeCurve + CurveArithmetic,
AffinePoint<C>:
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
FieldBytesSize<C>: sec1::ModulusSize,
SignatureSize<C>: ArrayLength<u8>,
{
Self::trial_recovery_from_digest(verifying_key, C::Digest::new_with_prefix(msg), signature)
}
/// Given a public key, message digest, and signature, use trial recovery
/// to determine if a suitable recovery ID exists, or return an error
/// otherwise.
pub fn trial_recovery_from_digest<C, D>(
verifying_key: &VerifyingKey<C>,
digest: D,
signature: &Signature<C>,
) -> Result<Self>
where
C: PrimeCurve + CurveArithmetic,
D: Digest,
AffinePoint<C>:
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
FieldBytesSize<C>: sec1::ModulusSize,
SignatureSize<C>: ArrayLength<u8>,
{
Self::trial_recovery_from_prehash(verifying_key, &digest.finalize(), signature)
}
/// Given a public key, message digest, and signature, use trial recovery
/// to determine if a suitable recovery ID exists, or return an error
/// otherwise.
pub fn trial_recovery_from_prehash<C>(
verifying_key: &VerifyingKey<C>,
prehash: &[u8],
signature: &Signature<C>,
) -> Result<Self>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>:
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
FieldBytesSize<C>: sec1::ModulusSize,
SignatureSize<C>: ArrayLength<u8>,
{
for id in 0..=Self::MAX {
let recovery_id = RecoveryId(id);
if let Ok(vk) = VerifyingKey::recover_from_prehash(prehash, signature, recovery_id) {
if verifying_key == &vk {
return Ok(recovery_id);
}
}
}
Err(Error::new())
}
}
impl TryFrom<u8> for RecoveryId {
type Error = Error;
fn try_from(byte: u8) -> Result<Self> {
Self::from_byte(byte).ok_or_else(Error::new)
}
}
impl From<RecoveryId> for u8 {
fn from(id: RecoveryId) -> u8 {
id.0
}
}
#[cfg(feature = "signing")]
impl<C> SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
/// Sign the given message prehash, returning a signature and recovery ID.
pub fn sign_prehash_recoverable(&self, prehash: &[u8]) -> Result<(Signature<C>, RecoveryId)> {
let z = bits2field::<C>(prehash)?;
let (sig, recid) = self
.as_nonzero_scalar()
.try_sign_prehashed_rfc6979::<C::Digest>(&z, &[])?;
Ok((sig, recid.ok_or_else(Error::new)?))
}
/// Sign the given message digest, returning a signature and recovery ID.
pub fn sign_digest_recoverable<D>(&self, msg_digest: D) -> Result<(Signature<C>, RecoveryId)>
where
D: Digest,
{
self.sign_prehash_recoverable(&msg_digest.finalize())
}
/// Sign the given message, hashing it with the curve's default digest
/// function, and returning a signature and recovery ID.
pub fn sign_recoverable(&self, msg: &[u8]) -> Result<(Signature<C>, RecoveryId)> {
self.sign_digest_recoverable(C::Digest::new_with_prefix(msg))
}
}
#[cfg(feature = "signing")]
impl<C, D> DigestSigner<D, (Signature<C>, RecoveryId)> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
D: Digest,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign_digest(&self, msg_digest: D) -> Result<(Signature<C>, RecoveryId)> {
self.sign_digest_recoverable(msg_digest)
}
}
#[cfg(feature = "signing")]
impl<C> PrehashSigner<(Signature<C>, RecoveryId)> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn sign_prehash(&self, prehash: &[u8]) -> Result<(Signature<C>, RecoveryId)> {
self.sign_prehash_recoverable(prehash)
}
}
#[cfg(feature = "signing")]
impl<C> Signer<(Signature<C>, RecoveryId)> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign(&self, msg: &[u8]) -> Result<(Signature<C>, RecoveryId)> {
self.sign_recoverable(msg)
}
}
#[cfg(feature = "verifying")]
impl<C> VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>:
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
FieldBytesSize<C>: sec1::ModulusSize,
SignatureSize<C>: ArrayLength<u8>,
{
/// Recover a [`VerifyingKey`] from the given message, signature, and
/// [`RecoveryId`].
///
/// The message is first hashed using this curve's [`DigestPrimitive`].
pub fn recover_from_msg(
msg: &[u8],
signature: &Signature<C>,
recovery_id: RecoveryId,
) -> Result<Self>
where
C: DigestPrimitive,
{
Self::recover_from_digest(C::Digest::new_with_prefix(msg), signature, recovery_id)
}
/// Recover a [`VerifyingKey`] from the given message [`Digest`],
/// signature, and [`RecoveryId`].
pub fn recover_from_digest<D>(
msg_digest: D,
signature: &Signature<C>,
recovery_id: RecoveryId,
) -> Result<Self>
where
D: Digest,
{
Self::recover_from_prehash(&msg_digest.finalize(), signature, recovery_id)
}
/// Recover a [`VerifyingKey`] from the given `prehash` of a message, the
/// signature over that prehashed message, and a [`RecoveryId`].
#[allow(non_snake_case)]
pub fn recover_from_prehash(
prehash: &[u8],
signature: &Signature<C>,
recovery_id: RecoveryId,
) -> Result<Self> {
let (r, s) = signature.split_scalars();
let z = <Scalar<C> as Reduce<C::Uint>>::reduce_bytes(&bits2field::<C>(prehash)?);
let mut r_bytes = r.to_repr();
if recovery_id.is_x_reduced() {
match Option::<C::Uint>::from(
C::Uint::decode_field_bytes(&r_bytes).checked_add(&C::ORDER),
) {
Some(restored) => r_bytes = restored.encode_field_bytes(),
// No reduction should happen here if r was reduced
None => return Err(Error::new()),
};
}
let R = AffinePoint::<C>::decompress(&r_bytes, u8::from(recovery_id.is_y_odd()).into());
if R.is_none().into() {
return Err(Error::new());
}
let R = ProjectivePoint::<C>::from(R.unwrap());
let r_inv = *r.invert();
let u1 = -(r_inv * z);
let u2 = r_inv * *s;
let pk = ProjectivePoint::<C>::lincomb(&ProjectivePoint::<C>::generator(), &u1, &R, &u2);
let vk = Self::from_affine(pk.into())?;
// Ensure signature verifies with the recovered key
vk.verify_prehash(prehash, signature)?;
Ok(vk)
}
}
#[cfg(test)]
mod tests {
use super::RecoveryId;
#[test]
fn new() {
assert_eq!(RecoveryId::new(false, false).to_byte(), 0);
assert_eq!(RecoveryId::new(true, false).to_byte(), 1);
assert_eq!(RecoveryId::new(false, true).to_byte(), 2);
assert_eq!(RecoveryId::new(true, true).to_byte(), 3);
}
#[test]
fn try_from() {
for n in 0u8..=3 {
assert_eq!(RecoveryId::try_from(n).unwrap().to_byte(), n);
}
for n in 4u8..=255 {
assert!(RecoveryId::try_from(n).is_err());
}
}
#[test]
fn is_x_reduced() {
assert_eq!(RecoveryId::try_from(0).unwrap().is_x_reduced(), false);
assert_eq!(RecoveryId::try_from(1).unwrap().is_x_reduced(), false);
assert_eq!(RecoveryId::try_from(2).unwrap().is_x_reduced(), true);
assert_eq!(RecoveryId::try_from(3).unwrap().is_x_reduced(), true);
}
#[test]
fn is_y_odd() {
assert_eq!(RecoveryId::try_from(0).unwrap().is_y_odd(), false);
assert_eq!(RecoveryId::try_from(1).unwrap().is_y_odd(), true);
assert_eq!(RecoveryId::try_from(2).unwrap().is_y_odd(), false);
assert_eq!(RecoveryId::try_from(3).unwrap().is_y_odd(), true);
}
}

598
vendor/ecdsa/src/signing.rs vendored Normal file
View File

@@ -0,0 +1,598 @@
//! ECDSA signing: producing signatures using a [`SigningKey`].
use crate::{
ecdsa_oid_for_digest,
hazmat::{bits2field, DigestPrimitive, SignPrimitive},
Error, Result, Signature, SignatureSize, SignatureWithOid,
};
use core::fmt::{self, Debug};
use digest::{const_oid::AssociatedOid, Digest, FixedOutput};
use elliptic_curve::{
generic_array::ArrayLength,
group::ff::PrimeField,
ops::Invert,
subtle::{Choice, ConstantTimeEq, CtOption},
zeroize::{Zeroize, ZeroizeOnDrop},
CurveArithmetic, FieldBytes, FieldBytesSize, NonZeroScalar, PrimeCurve, Scalar, SecretKey,
};
use signature::{
hazmat::{PrehashSigner, RandomizedPrehashSigner},
rand_core::CryptoRngCore,
DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer,
};
#[cfg(feature = "der")]
use {crate::der, core::ops::Add};
#[cfg(feature = "pem")]
use {
crate::elliptic_curve::pkcs8::{DecodePrivateKey, EncodePrivateKey, SecretDocument},
core::str::FromStr,
};
#[cfg(feature = "pkcs8")]
use crate::elliptic_curve::{
pkcs8::{
self,
der::AnyRef,
spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
ObjectIdentifier,
},
sec1::{self, FromEncodedPoint, ToEncodedPoint},
AffinePoint,
};
#[cfg(feature = "verifying")]
use {crate::VerifyingKey, elliptic_curve::PublicKey, signature::KeypairRef};
/// ECDSA secret key used for signing. Generic over prime order elliptic curves
/// (e.g. NIST P-curves)
///
/// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve, and a
/// [`SignPrimitive`] impl on its associated `Scalar` type.
///
/// ## Usage
///
/// The [`signature`] crate defines the following traits which are the
/// primary API for signing:
///
/// - [`Signer`]: sign a message using this key
/// - [`DigestSigner`]: sign the output of a [`Digest`] using this key
/// - [`PrehashSigner`]: sign the low-level raw output bytes of a message digest
///
/// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html)
/// for examples of using this type with a concrete elliptic curve.
#[derive(Clone)]
pub struct SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
/// ECDSA signing keys are non-zero elements of a given curve's scalar field.
secret_scalar: NonZeroScalar<C>,
/// Verifying key which corresponds to this signing key.
#[cfg(feature = "verifying")]
verifying_key: VerifyingKey<C>,
}
impl<C> SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
/// Generate a cryptographically random [`SigningKey`].
pub fn random(rng: &mut impl CryptoRngCore) -> Self {
NonZeroScalar::<C>::random(rng).into()
}
/// Initialize signing key from a raw scalar serialized as a byte array.
pub fn from_bytes(bytes: &FieldBytes<C>) -> Result<Self> {
SecretKey::<C>::from_bytes(bytes)
.map(Into::into)
.map_err(|_| Error::new())
}
/// Initialize signing key from a raw scalar serialized as a byte slice.
pub fn from_slice(bytes: &[u8]) -> Result<Self> {
SecretKey::<C>::from_slice(bytes)
.map(Into::into)
.map_err(|_| Error::new())
}
/// Serialize this [`SigningKey`] as bytes
pub fn to_bytes(&self) -> FieldBytes<C> {
self.secret_scalar.to_repr()
}
/// Borrow the secret [`NonZeroScalar`] value for this key.
///
/// # ⚠️ Warning
///
/// This value is key material.
///
/// Please treat it with the care it deserves!
pub fn as_nonzero_scalar(&self) -> &NonZeroScalar<C> {
&self.secret_scalar
}
/// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`].
#[cfg(feature = "verifying")]
pub fn verifying_key(&self) -> &VerifyingKey<C> {
&self.verifying_key
}
}
//
// `*Signer` trait impls
//
/// Sign message digest using a deterministic ephemeral scalar (`k`)
/// computed using the algorithm described in [RFC6979 § 3.2].
///
/// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
impl<C, D> DigestSigner<D, Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign_digest(&self, msg_digest: D) -> Result<Signature<C>> {
self.sign_prehash(&msg_digest.finalize_fixed())
}
}
/// Sign message prehash using a deterministic ephemeral scalar (`k`)
/// computed using the algorithm described in [RFC6979 § 3.2].
///
/// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
impl<C> PrehashSigner<Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn sign_prehash(&self, prehash: &[u8]) -> Result<Signature<C>> {
let z = bits2field::<C>(prehash)?;
Ok(self
.secret_scalar
.try_sign_prehashed_rfc6979::<C::Digest>(&z, &[])?
.0)
}
}
/// Sign message using a deterministic ephemeral scalar (`k`)
/// computed using the algorithm described in [RFC6979 § 3.2].
///
/// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3
impl<C> Signer<Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>> {
self.try_sign_digest(C::Digest::new_with_prefix(msg))
}
}
impl<C, D> RandomizedDigestSigner<D, Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg_digest: D,
) -> Result<Signature<C>> {
self.sign_prehash_with_rng(rng, &msg_digest.finalize_fixed())
}
}
impl<C> RandomizedPrehashSigner<Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn sign_prehash_with_rng(
&self,
rng: &mut impl CryptoRngCore,
prehash: &[u8],
) -> Result<Signature<C>> {
let z = bits2field::<C>(prehash)?;
let mut ad = FieldBytes::<C>::default();
rng.fill_bytes(&mut ad);
Ok(self
.secret_scalar
.try_sign_prehashed_rfc6979::<C::Digest>(&z, &ad)?
.0)
}
}
impl<C> RandomizedSigner<Signature<C>> for SigningKey<C>
where
Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign_with_rng(&self, rng: &mut impl CryptoRngCore, msg: &[u8]) -> Result<Signature<C>> {
self.try_sign_digest_with_rng(rng, C::Digest::new_with_prefix(msg))
}
}
impl<C, D> DigestSigner<D, SignatureWithOid<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
D: AssociatedOid + Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign_digest(&self, msg_digest: D) -> Result<SignatureWithOid<C>> {
let signature: Signature<C> = self.try_sign_digest(msg_digest)?;
let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?;
SignatureWithOid::new(signature, oid)
}
}
impl<C> Signer<SignatureWithOid<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
C::Digest: AssociatedOid,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn try_sign(&self, msg: &[u8]) -> Result<SignatureWithOid<C>> {
self.try_sign_digest(C::Digest::new_with_prefix(msg))
}
}
#[cfg(feature = "der")]
impl<C> PrehashSigner<der::Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn sign_prehash(&self, prehash: &[u8]) -> Result<der::Signature<C>> {
PrehashSigner::<Signature<C>>::sign_prehash(self, prehash).map(Into::into)
}
}
#[cfg(feature = "der")]
impl<C> Signer<der::Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn try_sign(&self, msg: &[u8]) -> Result<der::Signature<C>> {
Signer::<Signature<C>>::try_sign(self, msg).map(Into::into)
}
}
#[cfg(feature = "der")]
impl<C, D> RandomizedDigestSigner<D, der::Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn try_sign_digest_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg_digest: D,
) -> Result<der::Signature<C>> {
RandomizedDigestSigner::<D, Signature<C>>::try_sign_digest_with_rng(self, rng, msg_digest)
.map(Into::into)
}
}
#[cfg(feature = "der")]
impl<C> RandomizedPrehashSigner<der::Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn sign_prehash_with_rng(
&self,
rng: &mut impl CryptoRngCore,
prehash: &[u8],
) -> Result<der::Signature<C>> {
RandomizedPrehashSigner::<Signature<C>>::sign_prehash_with_rng(self, rng, prehash)
.map(Into::into)
}
}
#[cfg(feature = "der")]
impl<C> RandomizedSigner<der::Signature<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn try_sign_with_rng(
&self,
rng: &mut impl CryptoRngCore,
msg: &[u8],
) -> Result<der::Signature<C>> {
RandomizedSigner::<Signature<C>>::try_sign_with_rng(self, rng, msg).map(Into::into)
}
}
//
// Other trait impls
//
#[cfg(feature = "verifying")]
impl<C> AsRef<VerifyingKey<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn as_ref(&self) -> &VerifyingKey<C> {
&self.verifying_key
}
}
impl<C> ConstantTimeEq for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn ct_eq(&self, other: &Self) -> Choice {
self.secret_scalar.ct_eq(&other.secret_scalar)
}
}
impl<C> Debug for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("SigningKey").finish_non_exhaustive()
}
}
impl<C> Drop for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn drop(&mut self) {
self.secret_scalar.zeroize();
}
}
/// Constant-time comparison
impl<C> Eq for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
}
impl<C> PartialEq for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn eq(&self, other: &SigningKey<C>) -> bool {
self.ct_eq(other).into()
}
}
impl<C> From<NonZeroScalar<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(secret_scalar: NonZeroScalar<C>) -> Self {
#[cfg(feature = "verifying")]
let public_key = PublicKey::from_secret_scalar(&secret_scalar);
Self {
secret_scalar,
#[cfg(feature = "verifying")]
verifying_key: public_key.into(),
}
}
}
impl<C> From<SecretKey<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(secret_key: SecretKey<C>) -> Self {
Self::from(&secret_key)
}
}
impl<C> From<&SecretKey<C>> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(secret_key: &SecretKey<C>) -> Self {
secret_key.to_nonzero_scalar().into()
}
}
impl<C> From<SigningKey<C>> for SecretKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(key: SigningKey<C>) -> Self {
key.secret_scalar.into()
}
}
impl<C> From<&SigningKey<C>> for SecretKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(secret_key: &SigningKey<C>) -> Self {
secret_key.secret_scalar.into()
}
}
impl<C> TryFrom<&[u8]> for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
Self::from_slice(bytes)
}
}
impl<C> ZeroizeOnDrop for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
}
#[cfg(feature = "verifying")]
impl<C> From<SigningKey<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(signing_key: SigningKey<C>) -> VerifyingKey<C> {
signing_key.verifying_key
}
}
#[cfg(feature = "verifying")]
impl<C> From<&SigningKey<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn from(signing_key: &SigningKey<C>) -> VerifyingKey<C> {
signing_key.verifying_key
}
}
#[cfg(feature = "verifying")]
impl<C> KeypairRef for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type VerifyingKey = VerifyingKey<C>;
}
#[cfg(feature = "pkcs8")]
impl<C> AssociatedAlgorithmIdentifier for SigningKey<C>
where
C: AssociatedOid + CurveArithmetic + PrimeCurve,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type Params = ObjectIdentifier;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> =
SecretKey::<C>::ALGORITHM_IDENTIFIER;
}
#[cfg(feature = "pkcs8")]
impl<C> SignatureAlgorithmIdentifier for SigningKey<C>
where
C: PrimeCurve + CurveArithmetic,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
Signature::<C>::ALGORITHM_IDENTIFIER;
}
#[cfg(feature = "pkcs8")]
impl<C> TryFrom<pkcs8::PrivateKeyInfo<'_>> for SigningKey<C>
where
C: PrimeCurve + AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type Error = pkcs8::Error;
fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result<Self> {
SecretKey::try_from(private_key_info).map(Into::into)
}
}
#[cfg(feature = "pem")]
impl<C> EncodePrivateKey for SigningKey<C>
where
C: AssociatedOid + PrimeCurve + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn to_pkcs8_der(&self) -> pkcs8::Result<SecretDocument> {
SecretKey::from(self.secret_scalar).to_pkcs8_der()
}
}
#[cfg(feature = "pem")]
impl<C> FromStr for SigningKey<C>
where
C: PrimeCurve + AssociatedOid + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + SignPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::from_pkcs8_pem(s).map_err(|_| Error::new())
}
}

482
vendor/ecdsa/src/verifying.rs vendored Normal file
View File

@@ -0,0 +1,482 @@
//! ECDSA verifying: checking signatures are authentic using a [`VerifyingKey`].
use crate::{
hazmat::{bits2field, DigestPrimitive, VerifyPrimitive},
Error, Result, Signature, SignatureSize,
};
use core::{cmp::Ordering, fmt::Debug};
use elliptic_curve::{
generic_array::ArrayLength,
point::PointCompression,
sec1::{self, CompressedPoint, EncodedPoint, FromEncodedPoint, ToEncodedPoint},
AffinePoint, CurveArithmetic, FieldBytesSize, PrimeCurve, PublicKey,
};
use signature::{
digest::{Digest, FixedOutput},
hazmat::PrehashVerifier,
DigestVerifier, Verifier,
};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "der")]
use {crate::der, core::ops::Add};
#[cfg(feature = "pem")]
use {
core::str::FromStr,
elliptic_curve::pkcs8::{DecodePublicKey, EncodePublicKey},
};
#[cfg(feature = "pkcs8")]
use elliptic_curve::pkcs8::{
self,
der::AnyRef,
spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier},
AssociatedOid, ObjectIdentifier,
};
#[cfg(feature = "sha2")]
use {
crate::{
SignatureWithOid, ECDSA_SHA224_OID, ECDSA_SHA256_OID, ECDSA_SHA384_OID, ECDSA_SHA512_OID,
},
sha2::{Sha224, Sha256, Sha384, Sha512},
};
#[cfg(all(feature = "pem", feature = "serde"))]
use serdect::serde::{de, ser, Deserialize, Serialize};
/// ECDSA public key used for verifying signatures. Generic over prime order
/// elliptic curves (e.g. NIST P-curves)
///
/// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve, and a
/// [`VerifyPrimitive`] impl on its associated `AffinePoint` type.
///
/// ## Usage
///
/// The [`signature`] crate defines the following traits which are the
/// primary API for verifying:
///
/// - [`Verifier`]: verify a message against a provided key and signature
/// - [`DigestVerifier`]: verify a message [`Digest`] against a provided key and signature
/// - [`PrehashVerifier`]: verify the low-level raw output bytes of a message digest
///
/// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html)
/// for examples of using this type with a concrete elliptic curve.
///
/// # `serde` support
///
/// When the `serde` feature of this crate is enabled, it provides support for
/// serializing and deserializing ECDSA signatures using the `Serialize` and
/// `Deserialize` traits.
///
/// The serialization leverages the encoding used by the [`PublicKey`] type,
/// which is a binary-oriented ASN.1 DER encoding.
#[derive(Clone, Debug)]
pub struct VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
{
pub(crate) inner: PublicKey<C>,
}
impl<C> VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
/// Initialize [`VerifyingKey`] from a SEC1-encoded public key.
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
PublicKey::from_sec1_bytes(bytes)
.map(|pk| Self { inner: pk })
.map_err(|_| Error::new())
}
/// Initialize [`VerifyingKey`] from an affine point.
///
/// Returns an [`Error`] if the given affine point is the additive identity
/// (a.k.a. point at infinity).
pub fn from_affine(affine: AffinePoint<C>) -> Result<Self> {
Ok(Self {
inner: PublicKey::from_affine(affine).map_err(|_| Error::new())?,
})
}
/// Initialize [`VerifyingKey`] from an [`EncodedPoint`].
pub fn from_encoded_point(public_key: &EncodedPoint<C>) -> Result<Self> {
Option::from(PublicKey::<C>::from_encoded_point(public_key))
.map(|public_key| Self { inner: public_key })
.ok_or_else(Error::new)
}
/// Serialize this [`VerifyingKey`] as a SEC1 [`EncodedPoint`], optionally
/// applying point compression.
pub fn to_encoded_point(&self, compress: bool) -> EncodedPoint<C> {
self.inner.to_encoded_point(compress)
}
/// Convert this [`VerifyingKey`] into the
/// `Elliptic-Curve-Point-to-Octet-String` encoding described in
/// SEC 1: Elliptic Curve Cryptography (Version 2.0) section 2.3.3
/// (page 10).
///
/// <http://www.secg.org/sec1-v2.pdf>
#[cfg(feature = "alloc")]
pub fn to_sec1_bytes(&self) -> Box<[u8]>
where
C: PointCompression,
{
self.inner.to_sec1_bytes()
}
/// Borrow the inner [`AffinePoint`] for this public key.
pub fn as_affine(&self) -> &AffinePoint<C> {
self.inner.as_affine()
}
}
//
// `*Verifier` trait impls
//
impl<C, D> DigestVerifier<D, Signature<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
AffinePoint<C>: VerifyPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn verify_digest(&self, msg_digest: D, signature: &Signature<C>) -> Result<()> {
self.inner.as_affine().verify_digest(msg_digest, signature)
}
}
impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>: VerifyPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn verify_prehash(&self, prehash: &[u8], signature: &Signature<C>) -> Result<()> {
let field = bits2field::<C>(prehash)?;
self.inner.as_affine().verify_prehashed(&field, signature)
}
}
impl<C> Verifier<Signature<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
AffinePoint<C>: VerifyPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn verify(&self, msg: &[u8], signature: &Signature<C>) -> Result<()> {
self.verify_digest(C::Digest::new_with_prefix(msg), signature)
}
}
#[cfg(feature = "sha2")]
impl<C> Verifier<SignatureWithOid<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
AffinePoint<C>: VerifyPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
{
fn verify(&self, msg: &[u8], sig: &SignatureWithOid<C>) -> Result<()> {
match sig.oid() {
ECDSA_SHA224_OID => self.verify_prehash(&Sha224::digest(msg), sig.signature()),
ECDSA_SHA256_OID => self.verify_prehash(&Sha256::digest(msg), sig.signature()),
ECDSA_SHA384_OID => self.verify_prehash(&Sha384::digest(msg), sig.signature()),
ECDSA_SHA512_OID => self.verify_prehash(&Sha512::digest(msg), sig.signature()),
_ => Err(Error::new()),
}
}
}
#[cfg(feature = "der")]
impl<C, D> DigestVerifier<D, der::Signature<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
D: Digest + FixedOutput<OutputSize = FieldBytesSize<C>>,
AffinePoint<C>: VerifyPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn verify_digest(&self, msg_digest: D, signature: &der::Signature<C>) -> Result<()> {
let signature = Signature::<C>::try_from(signature.clone())?;
DigestVerifier::<D, Signature<C>>::verify_digest(self, msg_digest, &signature)
}
}
#[cfg(feature = "der")]
impl<C> PrehashVerifier<der::Signature<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
AffinePoint<C>: VerifyPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn verify_prehash(&self, prehash: &[u8], signature: &der::Signature<C>) -> Result<()> {
let signature = Signature::<C>::try_from(signature.clone())?;
PrehashVerifier::<Signature<C>>::verify_prehash(self, prehash, &signature)
}
}
#[cfg(feature = "der")]
impl<C> Verifier<der::Signature<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic + DigestPrimitive,
AffinePoint<C>: VerifyPrimitive<C>,
SignatureSize<C>: ArrayLength<u8>,
der::MaxSize<C>: ArrayLength<u8>,
<FieldBytesSize<C> as Add>::Output: Add<der::MaxOverhead> + ArrayLength<u8>,
{
fn verify(&self, msg: &[u8], signature: &der::Signature<C>) -> Result<()> {
let signature = Signature::<C>::try_from(signature.clone())?;
Verifier::<Signature<C>>::verify(self, msg, &signature)
}
}
//
// Other trait impls
//
impl<C> AsRef<AffinePoint<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn as_ref(&self) -> &AffinePoint<C> {
self.as_affine()
}
}
impl<C> Copy for VerifyingKey<C> where C: PrimeCurve + CurveArithmetic {}
impl<C> From<VerifyingKey<C>> for CompressedPoint<C>
where
C: PrimeCurve + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn from(verifying_key: VerifyingKey<C>) -> CompressedPoint<C> {
verifying_key.inner.into()
}
}
impl<C> From<&VerifyingKey<C>> for CompressedPoint<C>
where
C: PrimeCurve + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn from(verifying_key: &VerifyingKey<C>) -> CompressedPoint<C> {
verifying_key.inner.into()
}
}
impl<C> From<VerifyingKey<C>> for EncodedPoint<C>
where
C: PrimeCurve + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn from(verifying_key: VerifyingKey<C>) -> EncodedPoint<C> {
verifying_key.inner.into()
}
}
impl<C> From<&VerifyingKey<C>> for EncodedPoint<C>
where
C: PrimeCurve + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn from(verifying_key: &VerifyingKey<C>) -> EncodedPoint<C> {
verifying_key.inner.into()
}
}
impl<C> Eq for VerifyingKey<C> where C: PrimeCurve + CurveArithmetic {}
impl<C> PartialEq for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
{
fn eq(&self, other: &Self) -> bool {
self.inner.eq(&other.inner)
}
}
impl<C> From<PublicKey<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
{
fn from(public_key: PublicKey<C>) -> VerifyingKey<C> {
VerifyingKey { inner: public_key }
}
}
impl<C> From<&PublicKey<C>> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
{
fn from(public_key: &PublicKey<C>) -> VerifyingKey<C> {
(*public_key).into()
}
}
impl<C> From<VerifyingKey<C>> for PublicKey<C>
where
C: PrimeCurve + CurveArithmetic,
{
fn from(verifying_key: VerifyingKey<C>) -> PublicKey<C> {
verifying_key.inner
}
}
impl<C> From<&VerifyingKey<C>> for PublicKey<C>
where
C: PrimeCurve + CurveArithmetic,
{
fn from(verifying_key: &VerifyingKey<C>) -> PublicKey<C> {
(*verifying_key).into()
}
}
impl<C> PartialOrd for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
self.inner.partial_cmp(&other.inner)
}
}
impl<C> Ord for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn cmp(&self, other: &Self) -> Ordering {
self.inner.cmp(&other.inner)
}
}
impl<C> TryFrom<&[u8]> for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
Self::from_sec1_bytes(bytes)
}
}
#[cfg(feature = "pkcs8")]
impl<C> AssociatedAlgorithmIdentifier for VerifyingKey<C>
where
C: AssociatedOid + CurveArithmetic + PrimeCurve,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
type Params = ObjectIdentifier;
const ALGORITHM_IDENTIFIER: AlgorithmIdentifier<ObjectIdentifier> =
PublicKey::<C>::ALGORITHM_IDENTIFIER;
}
#[cfg(feature = "pkcs8")]
impl<C> SignatureAlgorithmIdentifier for VerifyingKey<C>
where
C: PrimeCurve + CurveArithmetic,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
Signature<C>: AssociatedAlgorithmIdentifier<Params = AnyRef<'static>>,
{
type Params = AnyRef<'static>;
const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier<Self::Params> =
Signature::<C>::ALGORITHM_IDENTIFIER;
}
#[cfg(feature = "pkcs8")]
impl<C> TryFrom<pkcs8::SubjectPublicKeyInfoRef<'_>> for VerifyingKey<C>
where
C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
type Error = pkcs8::spki::Error;
fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
PublicKey::try_from(spki).map(|inner| Self { inner })
}
}
#[cfg(feature = "pem")]
impl<C> EncodePublicKey for VerifyingKey<C>
where
C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
self.inner.to_public_key_der()
}
}
#[cfg(feature = "pem")]
impl<C> FromStr for VerifyingKey<C>
where
C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
type Err = Error;
fn from_str(s: &str) -> Result<Self> {
Self::from_public_key_pem(s).map_err(|_| Error::new())
}
}
#[cfg(all(feature = "pem", feature = "serde"))]
impl<C> Serialize for VerifyingKey<C>
where
C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
where
S: ser::Serializer,
{
self.inner.serialize(serializer)
}
}
#[cfg(all(feature = "pem", feature = "serde"))]
impl<'de, C> Deserialize<'de> for VerifyingKey<C>
where
C: PrimeCurve + AssociatedOid + CurveArithmetic + PointCompression,
AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
FieldBytesSize<C>: sec1::ModulusSize,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
PublicKey::<C>::deserialize(deserializer).map(Into::into)
}
}