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

82
vendor/sec1/src/error.rs vendored Normal file
View File

@@ -0,0 +1,82 @@
//! Error types
use core::fmt;
#[cfg(feature = "pem")]
use der::pem;
/// Result type with `sec1` crate's [`Error`] type.
pub type Result<T> = core::result::Result<T, Error>;
/// Error type
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[non_exhaustive]
pub enum Error {
/// ASN.1 DER-related errors.
#[cfg(feature = "der")]
Asn1(der::Error),
/// Cryptographic errors.
///
/// These can be used by EC implementations to signal that a key is
/// invalid for cryptographic reasons. This means the document parsed
/// correctly, but one of the values contained within was invalid, e.g.
/// a number expected to be a prime was not a prime.
Crypto,
/// PKCS#8 errors.
#[cfg(feature = "pkcs8")]
Pkcs8(pkcs8::Error),
/// Errors relating to the `Elliptic-Curve-Point-to-Octet-String` or
/// `Octet-String-to-Elliptic-Curve-Point` encodings.
PointEncoding,
/// Version errors
Version,
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
#[cfg(feature = "der")]
Error::Asn1(err) => write!(f, "SEC1 ASN.1 error: {}", err),
Error::Crypto => f.write_str("SEC1 cryptographic error"),
#[cfg(feature = "pkcs8")]
Error::Pkcs8(err) => write!(f, "{}", err),
Error::PointEncoding => f.write_str("elliptic curve point encoding error"),
Error::Version => f.write_str("SEC1 version error"),
}
}
}
#[cfg(feature = "der")]
impl From<der::Error> for Error {
fn from(err: der::Error) -> Error {
Error::Asn1(err)
}
}
#[cfg(feature = "pem")]
impl From<pem::Error> for Error {
fn from(err: pem::Error) -> Error {
der::Error::from(err).into()
}
}
#[cfg(feature = "pkcs8")]
impl From<pkcs8::Error> for Error {
fn from(err: pkcs8::Error) -> Error {
Error::Pkcs8(err)
}
}
#[cfg(feature = "pkcs8")]
impl From<pkcs8::spki::Error> for Error {
fn from(err: pkcs8::spki::Error) -> Error {
Error::Pkcs8(pkcs8::Error::PublicKey(err))
}
}
#[cfg(feature = "std")]
impl std::error::Error for Error {}

77
vendor/sec1/src/lib.rs vendored Normal file
View File

@@ -0,0 +1,77 @@
#![no_std]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#![doc = include_str!("../README.md")]
#![doc(
html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg",
html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg"
)]
#![forbid(unsafe_code)]
#![warn(
clippy::mod_module_files,
clippy::unwrap_used,
missing_docs,
rust_2018_idioms,
unused_qualifications
)]
//! ## `serde` support
//!
//! When the `serde` feature of this crate is enabled, the [`EncodedPoint`]
//! type receives impls of [`serde::Serialize`] and [`serde::Deserialize`].
//!
//! Additionally, when both the `alloc` and `serde` features are enabled, the
//! serializers/deserializers will autodetect if a "human friendly" textual
//! encoding is being used, and if so encode the points as hexadecimal.
#[cfg(feature = "alloc")]
#[allow(unused_extern_crates)]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
#[cfg(feature = "point")]
pub mod point;
mod error;
#[cfg(feature = "der")]
mod parameters;
#[cfg(feature = "der")]
mod private_key;
#[cfg(feature = "der")]
mod traits;
#[cfg(feature = "der")]
pub use der;
pub use crate::error::{Error, Result};
#[cfg(feature = "point")]
pub use crate::point::EncodedPoint;
#[cfg(feature = "point")]
pub use generic_array::typenum::consts;
#[cfg(feature = "der")]
pub use crate::{parameters::EcParameters, private_key::EcPrivateKey, traits::DecodeEcPrivateKey};
#[cfg(all(feature = "alloc", feature = "der"))]
pub use crate::traits::EncodeEcPrivateKey;
#[cfg(feature = "pem")]
pub use der::pem::{self, LineEnding};
#[cfg(feature = "pkcs8")]
pub use pkcs8;
#[cfg(feature = "pkcs8")]
use pkcs8::ObjectIdentifier;
#[cfg(all(doc, feature = "serde"))]
use serdect::serde;
/// Algorithm [`ObjectIdentifier`] for elliptic curve public key cryptography
/// (`id-ecPublicKey`).
///
/// <http://oid-info.com/get/1.2.840.10045.2.1>
#[cfg(feature = "pkcs8")]
pub const ALGORITHM_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.10045.2.1");

75
vendor/sec1/src/parameters.rs vendored Normal file
View File

@@ -0,0 +1,75 @@
use der::{
asn1::{AnyRef, ObjectIdentifier},
DecodeValue, EncodeValue, FixedTag, Header, Length, Reader, Tag, Writer,
};
/// Elliptic curve parameters as described in
/// [RFC5480 Section 2.1.1](https://datatracker.ietf.org/doc/html/rfc5480#section-2.1.1):
///
/// ```text
/// ECParameters ::= CHOICE {
/// namedCurve OBJECT IDENTIFIER
/// -- implicitCurve NULL
/// -- specifiedCurve SpecifiedECDomain
/// }
/// -- implicitCurve and specifiedCurve MUST NOT be used in PKIX.
/// -- Details for SpecifiedECDomain can be found in [X9.62].
/// -- Any future additions to this CHOICE should be coordinated
/// -- with ANSI X9.
/// ```
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum EcParameters {
/// Elliptic curve named by a particular OID.
///
/// > namedCurve identifies all the required values for a particular
/// > set of elliptic curve domain parameters to be represented by an
/// > object identifier.
NamedCurve(ObjectIdentifier),
}
impl<'a> DecodeValue<'a> for EcParameters {
fn decode_value<R: Reader<'a>>(decoder: &mut R, header: Header) -> der::Result<Self> {
ObjectIdentifier::decode_value(decoder, header).map(Self::NamedCurve)
}
}
impl EncodeValue for EcParameters {
fn value_len(&self) -> der::Result<Length> {
match self {
Self::NamedCurve(oid) => oid.value_len(),
}
}
fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
match self {
Self::NamedCurve(oid) => oid.encode_value(writer),
}
}
}
impl EcParameters {
/// Obtain the `namedCurve` OID.
pub fn named_curve(self) -> Option<ObjectIdentifier> {
match self {
Self::NamedCurve(oid) => Some(oid),
}
}
}
impl<'a> From<&'a EcParameters> for AnyRef<'a> {
fn from(params: &'a EcParameters) -> AnyRef<'a> {
match params {
EcParameters::NamedCurve(oid) => oid.into(),
}
}
}
impl From<ObjectIdentifier> for EcParameters {
fn from(oid: ObjectIdentifier) -> EcParameters {
EcParameters::NamedCurve(oid)
}
}
impl FixedTag for EcParameters {
const TAG: Tag = Tag::ObjectIdentifier;
}

776
vendor/sec1/src/point.rs vendored Normal file
View File

@@ -0,0 +1,776 @@
//! Support for the SEC1 `Elliptic-Curve-Point-to-Octet-String` and
//! `Octet-String-to-Elliptic-Curve-Point` encoding algorithms.
//!
//! Described in [SEC1: Elliptic Curve Cryptography] (Version 2.0) section 2.3.3 (p.10).
//!
//! [SEC1: Elliptic Curve Cryptography]: https://www.secg.org/sec1-v2.pdf
use crate::{Error, Result};
use base16ct::HexDisplay;
use core::{
cmp::Ordering,
fmt::{self, Debug},
hash::{Hash, Hasher},
ops::Add,
str,
};
use generic_array::{
typenum::{U1, U24, U28, U32, U48, U66},
ArrayLength, GenericArray,
};
#[cfg(feature = "alloc")]
use alloc::boxed::Box;
#[cfg(feature = "serde")]
use serdect::serde::{de, ser, Deserialize, Serialize};
#[cfg(feature = "subtle")]
use subtle::{Choice, ConditionallySelectable};
#[cfg(feature = "zeroize")]
use zeroize::Zeroize;
/// Trait for supported modulus sizes which precomputes the typenums for
/// various point encodings so they don't need to be included as bounds.
// TODO(tarcieri): replace this all with const generic expressions.
pub trait ModulusSize: 'static + ArrayLength<u8> + Copy + Debug {
/// Size of a compressed point for the given elliptic curve when encoded
/// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm
/// (including leading `0x02` or `0x03` tag byte).
type CompressedPointSize: 'static + ArrayLength<u8> + Copy + Debug;
/// Size of an uncompressed point for the given elliptic curve when encoded
/// using the SEC1 `Elliptic-Curve-Point-to-Octet-String` algorithm
/// (including leading `0x04` tag byte).
type UncompressedPointSize: 'static + ArrayLength<u8> + Copy + Debug;
/// Size of an untagged point for given elliptic curve, i.e. size of two
/// serialized base field elements.
type UntaggedPointSize: 'static + ArrayLength<u8> + Copy + Debug;
}
macro_rules! impl_modulus_size {
($($size:ty),+) => {
$(impl ModulusSize for $size {
type CompressedPointSize = <$size as Add<U1>>::Output;
type UncompressedPointSize = <Self::UntaggedPointSize as Add<U1>>::Output;
type UntaggedPointSize = <$size as Add>::Output;
})+
}
}
impl_modulus_size!(U24, U28, U32, U48, U66);
/// SEC1 encoded curve point.
///
/// This type is an enum over the compressed and uncompressed encodings,
/// useful for cases where either encoding can be supported, or conversions
/// between the two forms.
#[derive(Clone, Default)]
pub struct EncodedPoint<Size>
where
Size: ModulusSize,
{
bytes: GenericArray<u8, Size::UncompressedPointSize>,
}
#[allow(clippy::len_without_is_empty)]
impl<Size> EncodedPoint<Size>
where
Size: ModulusSize,
{
/// Decode elliptic curve point (compressed or uncompressed) from 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>
pub fn from_bytes(input: impl AsRef<[u8]>) -> Result<Self> {
let input = input.as_ref();
// Validate tag
let tag = input
.first()
.cloned()
.ok_or(Error::PointEncoding)
.and_then(Tag::from_u8)?;
// Validate length
let expected_len = tag.message_len(Size::to_usize());
if input.len() != expected_len {
return Err(Error::PointEncoding);
}
let mut bytes = GenericArray::default();
bytes[..expected_len].copy_from_slice(input);
Ok(Self { bytes })
}
/// Decode elliptic curve point from raw uncompressed coordinates, i.e.
/// encoded as the concatenated `x || y` coordinates with no leading SEC1
/// tag byte (which would otherwise be `0x04` for an uncompressed point).
pub fn from_untagged_bytes(bytes: &GenericArray<u8, Size::UntaggedPointSize>) -> Self {
let (x, y) = bytes.split_at(Size::to_usize());
Self::from_affine_coordinates(x.into(), y.into(), false)
}
/// Encode an elliptic curve point from big endian serialized coordinates
/// (with optional point compression)
pub fn from_affine_coordinates(
x: &GenericArray<u8, Size>,
y: &GenericArray<u8, Size>,
compress: bool,
) -> Self {
let tag = if compress {
Tag::compress_y(y.as_slice())
} else {
Tag::Uncompressed
};
let mut bytes = GenericArray::default();
bytes[0] = tag.into();
bytes[1..(Size::to_usize() + 1)].copy_from_slice(x);
if !compress {
bytes[(Size::to_usize() + 1)..].copy_from_slice(y);
}
Self { bytes }
}
/// Return [`EncodedPoint`] representing the additive identity
/// (a.k.a. point at infinity)
pub fn identity() -> Self {
Self::default()
}
/// Get the length of the encoded point in bytes
pub fn len(&self) -> usize {
self.tag().message_len(Size::to_usize())
}
/// Get byte slice containing the serialized [`EncodedPoint`].
pub fn as_bytes(&self) -> &[u8] {
&self.bytes[..self.len()]
}
/// Get boxed byte slice containing the serialized [`EncodedPoint`]
#[cfg(feature = "alloc")]
pub fn to_bytes(&self) -> Box<[u8]> {
self.as_bytes().to_vec().into_boxed_slice()
}
/// Is this [`EncodedPoint`] compact?
pub fn is_compact(&self) -> bool {
self.tag().is_compact()
}
/// Is this [`EncodedPoint`] compressed?
pub fn is_compressed(&self) -> bool {
self.tag().is_compressed()
}
/// Is this [`EncodedPoint`] the additive identity? (a.k.a. point at infinity)
pub fn is_identity(&self) -> bool {
self.tag().is_identity()
}
/// Compress this [`EncodedPoint`], returning a new [`EncodedPoint`].
pub fn compress(&self) -> Self {
match self.coordinates() {
Coordinates::Compressed { .. }
| Coordinates::Compact { .. }
| Coordinates::Identity => self.clone(),
Coordinates::Uncompressed { x, y } => Self::from_affine_coordinates(x, y, true),
}
}
/// Get the SEC1 tag for this [`EncodedPoint`]
pub fn tag(&self) -> Tag {
// Tag is ensured valid by the constructor
Tag::from_u8(self.bytes[0]).expect("invalid tag")
}
/// Get the [`Coordinates`] for this [`EncodedPoint`].
#[inline]
pub fn coordinates(&self) -> Coordinates<'_, Size> {
if self.is_identity() {
return Coordinates::Identity;
}
let (x, y) = self.bytes[1..].split_at(Size::to_usize());
if self.is_compressed() {
Coordinates::Compressed {
x: x.into(),
y_is_odd: self.tag() as u8 & 1 == 1,
}
} else if self.is_compact() {
Coordinates::Compact { x: x.into() }
} else {
Coordinates::Uncompressed {
x: x.into(),
y: y.into(),
}
}
}
/// Get the x-coordinate for this [`EncodedPoint`].
///
/// Returns `None` if this point is the identity point.
pub fn x(&self) -> Option<&GenericArray<u8, Size>> {
match self.coordinates() {
Coordinates::Identity => None,
Coordinates::Compressed { x, .. } => Some(x),
Coordinates::Uncompressed { x, .. } => Some(x),
Coordinates::Compact { x } => Some(x),
}
}
/// Get the y-coordinate for this [`EncodedPoint`].
///
/// Returns `None` if this point is compressed or the identity point.
pub fn y(&self) -> Option<&GenericArray<u8, Size>> {
match self.coordinates() {
Coordinates::Compressed { .. } | Coordinates::Identity => None,
Coordinates::Uncompressed { y, .. } => Some(y),
Coordinates::Compact { .. } => None,
}
}
}
impl<Size> AsRef<[u8]> for EncodedPoint<Size>
where
Size: ModulusSize,
{
#[inline]
fn as_ref(&self) -> &[u8] {
self.as_bytes()
}
}
#[cfg(feature = "subtle")]
impl<Size> ConditionallySelectable for EncodedPoint<Size>
where
Size: ModulusSize,
<Size::UncompressedPointSize as ArrayLength<u8>>::ArrayType: Copy,
{
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut bytes = GenericArray::default();
for (i, byte) in bytes.iter_mut().enumerate() {
*byte = u8::conditional_select(&a.bytes[i], &b.bytes[i], choice);
}
Self { bytes }
}
}
impl<Size> Copy for EncodedPoint<Size>
where
Size: ModulusSize,
<Size::UncompressedPointSize as ArrayLength<u8>>::ArrayType: Copy,
{
}
impl<Size> Debug for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "EncodedPoint({:?})", self.coordinates())
}
}
impl<Size: ModulusSize> Eq for EncodedPoint<Size> {}
impl<Size> PartialEq for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn eq(&self, other: &Self) -> bool {
self.as_bytes() == other.as_bytes()
}
}
impl<Size> Hash for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn hash<H: Hasher>(&self, state: &mut H) {
self.as_bytes().hash(state)
}
}
impl<Size: ModulusSize> PartialOrd for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<Size: ModulusSize> Ord for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn cmp(&self, other: &Self) -> Ordering {
self.as_bytes().cmp(other.as_bytes())
}
}
impl<Size: ModulusSize> TryFrom<&[u8]> for EncodedPoint<Size>
where
Size: ModulusSize,
{
type Error = Error;
fn try_from(bytes: &[u8]) -> Result<Self> {
Self::from_bytes(bytes)
}
}
#[cfg(feature = "zeroize")]
impl<Size> Zeroize for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn zeroize(&mut self) {
self.bytes.zeroize();
*self = Self::identity();
}
}
impl<Size> fmt::Display for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:X}", self)
}
}
impl<Size> fmt::LowerHex for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:x}", HexDisplay(self.as_bytes()))
}
}
impl<Size> fmt::UpperHex for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:X}", HexDisplay(self.as_bytes()))
}
}
/// Decode a SEC1-encoded point from hexadecimal.
///
/// Upper and lower case hexadecimal are both accepted, however mixed case is
/// rejected.
impl<Size> str::FromStr for EncodedPoint<Size>
where
Size: ModulusSize,
{
type Err = Error;
fn from_str(hex: &str) -> Result<Self> {
let mut buf = GenericArray::<u8, Size::UncompressedPointSize>::default();
base16ct::mixed::decode(hex, &mut buf)
.map_err(|_| Error::PointEncoding)
.and_then(Self::from_bytes)
}
}
#[cfg(feature = "serde")]
impl<Size> Serialize for EncodedPoint<Size>
where
Size: ModulusSize,
{
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, Size> Deserialize<'de> for EncodedPoint<Size>
where
Size: ModulusSize,
{
fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
where
D: de::Deserializer<'de>,
{
let bytes = serdect::slice::deserialize_hex_or_bin_vec(deserializer)?;
Self::from_bytes(bytes).map_err(de::Error::custom)
}
}
/// Enum representing the coordinates of either compressed or uncompressed
/// SEC1-encoded elliptic curve points.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Coordinates<'a, Size: ModulusSize> {
/// Identity point (a.k.a. point at infinity)
Identity,
/// Compact curve point
Compact {
/// x-coordinate
x: &'a GenericArray<u8, Size>,
},
/// Compressed curve point
Compressed {
/// x-coordinate
x: &'a GenericArray<u8, Size>,
/// Is the y-coordinate odd?
y_is_odd: bool,
},
/// Uncompressed curve point
Uncompressed {
/// x-coordinate
x: &'a GenericArray<u8, Size>,
/// y-coordinate
y: &'a GenericArray<u8, Size>,
},
}
impl<'a, Size: ModulusSize> Coordinates<'a, Size> {
/// Get the tag octet needed to encode this set of [`Coordinates`]
pub fn tag(&self) -> Tag {
match self {
Coordinates::Compact { .. } => Tag::Compact,
Coordinates::Compressed { y_is_odd, .. } => {
if *y_is_odd {
Tag::CompressedOddY
} else {
Tag::CompressedEvenY
}
}
Coordinates::Identity => Tag::Identity,
Coordinates::Uncompressed { .. } => Tag::Uncompressed,
}
}
}
/// Tag byte used by the `Elliptic-Curve-Point-to-Octet-String` encoding.
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[repr(u8)]
pub enum Tag {
/// Identity point (`0x00`)
Identity = 0,
/// Compressed point with even y-coordinate (`0x02`)
CompressedEvenY = 2,
/// Compressed point with odd y-coordinate (`0x03`)
CompressedOddY = 3,
/// Uncompressed point (`0x04`)
Uncompressed = 4,
/// Compact point (`0x05`)
Compact = 5,
}
impl Tag {
/// Parse a tag value from a byte
pub fn from_u8(byte: u8) -> Result<Self> {
match byte {
0 => Ok(Tag::Identity),
2 => Ok(Tag::CompressedEvenY),
3 => Ok(Tag::CompressedOddY),
4 => Ok(Tag::Uncompressed),
5 => Ok(Tag::Compact),
_ => Err(Error::PointEncoding),
}
}
/// Is this point compact?
pub fn is_compact(self) -> bool {
matches!(self, Tag::Compact)
}
/// Is this point compressed?
pub fn is_compressed(self) -> bool {
matches!(self, Tag::CompressedEvenY | Tag::CompressedOddY)
}
/// Is this point the identity point?
pub fn is_identity(self) -> bool {
self == Tag::Identity
}
/// Compute the expected total message length for a message prefixed
/// with this tag (including the tag byte), given the field element size
/// (in bytes) for a particular elliptic curve.
pub fn message_len(self, field_element_size: usize) -> usize {
1 + match self {
Tag::Identity => 0,
Tag::CompressedEvenY | Tag::CompressedOddY => field_element_size,
Tag::Uncompressed => field_element_size * 2,
Tag::Compact => field_element_size,
}
}
/// Compress the given y-coordinate, returning a `Tag::Compressed*` value
fn compress_y(y: &[u8]) -> Self {
// Is the y-coordinate odd in the SEC1 sense: `self mod 2 == 1`?
if y.as_ref().last().expect("empty y-coordinate") & 1 == 1 {
Tag::CompressedOddY
} else {
Tag::CompressedEvenY
}
}
}
impl TryFrom<u8> for Tag {
type Error = Error;
fn try_from(byte: u8) -> Result<Self> {
Self::from_u8(byte)
}
}
impl From<Tag> for u8 {
fn from(tag: Tag) -> u8 {
tag as u8
}
}
#[cfg(test)]
mod tests {
use super::{Coordinates, Tag};
use core::str::FromStr;
use generic_array::{typenum::U32, GenericArray};
use hex_literal::hex;
#[cfg(feature = "alloc")]
use alloc::string::ToString;
#[cfg(feature = "subtle")]
use subtle::ConditionallySelectable;
type EncodedPoint = super::EncodedPoint<U32>;
/// Identity point
const IDENTITY_BYTES: [u8; 1] = [0];
/// Example uncompressed point
const UNCOMPRESSED_BYTES: [u8; 65] = hex!("0411111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222");
/// Example compressed point: `UNCOMPRESSED_BYTES` after point compression
const COMPRESSED_BYTES: [u8; 33] =
hex!("021111111111111111111111111111111111111111111111111111111111111111");
#[test]
fn decode_compressed_point() {
// Even y-coordinate
let compressed_even_y_bytes =
hex!("020100000000000000000000000000000000000000000000000000000000000000");
let compressed_even_y = EncodedPoint::from_bytes(&compressed_even_y_bytes[..]).unwrap();
assert!(compressed_even_y.is_compressed());
assert_eq!(compressed_even_y.tag(), Tag::CompressedEvenY);
assert_eq!(compressed_even_y.len(), 33);
assert_eq!(compressed_even_y.as_bytes(), &compressed_even_y_bytes[..]);
assert_eq!(
compressed_even_y.coordinates(),
Coordinates::Compressed {
x: &hex!("0100000000000000000000000000000000000000000000000000000000000000").into(),
y_is_odd: false
}
);
assert_eq!(
compressed_even_y.x().unwrap(),
&hex!("0100000000000000000000000000000000000000000000000000000000000000").into()
);
assert_eq!(compressed_even_y.y(), None);
// Odd y-coordinate
let compressed_odd_y_bytes =
hex!("030200000000000000000000000000000000000000000000000000000000000000");
let compressed_odd_y = EncodedPoint::from_bytes(&compressed_odd_y_bytes[..]).unwrap();
assert!(compressed_odd_y.is_compressed());
assert_eq!(compressed_odd_y.tag(), Tag::CompressedOddY);
assert_eq!(compressed_odd_y.len(), 33);
assert_eq!(compressed_odd_y.as_bytes(), &compressed_odd_y_bytes[..]);
assert_eq!(
compressed_odd_y.coordinates(),
Coordinates::Compressed {
x: &hex!("0200000000000000000000000000000000000000000000000000000000000000").into(),
y_is_odd: true
}
);
assert_eq!(
compressed_odd_y.x().unwrap(),
&hex!("0200000000000000000000000000000000000000000000000000000000000000").into()
);
assert_eq!(compressed_odd_y.y(), None);
}
#[test]
fn decode_uncompressed_point() {
let uncompressed_point = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap();
assert!(!uncompressed_point.is_compressed());
assert_eq!(uncompressed_point.tag(), Tag::Uncompressed);
assert_eq!(uncompressed_point.len(), 65);
assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]);
assert_eq!(
uncompressed_point.coordinates(),
Coordinates::Uncompressed {
x: &hex!("1111111111111111111111111111111111111111111111111111111111111111").into(),
y: &hex!("2222222222222222222222222222222222222222222222222222222222222222").into()
}
);
assert_eq!(
uncompressed_point.x().unwrap(),
&hex!("1111111111111111111111111111111111111111111111111111111111111111").into()
);
assert_eq!(
uncompressed_point.y().unwrap(),
&hex!("2222222222222222222222222222222222222222222222222222222222222222").into()
);
}
#[test]
fn decode_identity() {
let identity_point = EncodedPoint::from_bytes(&IDENTITY_BYTES[..]).unwrap();
assert!(identity_point.is_identity());
assert_eq!(identity_point.tag(), Tag::Identity);
assert_eq!(identity_point.len(), 1);
assert_eq!(identity_point.as_bytes(), &IDENTITY_BYTES[..]);
assert_eq!(identity_point.coordinates(), Coordinates::Identity);
assert_eq!(identity_point.x(), None);
assert_eq!(identity_point.y(), None);
}
#[test]
fn decode_invalid_tag() {
let mut compressed_bytes = COMPRESSED_BYTES;
let mut uncompressed_bytes = UNCOMPRESSED_BYTES;
for bytes in &mut [&mut compressed_bytes[..], &mut uncompressed_bytes[..]] {
for tag in 0..=0xFF {
// valid tags
if tag == 2 || tag == 3 || tag == 4 || tag == 5 {
continue;
}
(*bytes)[0] = tag;
let decode_result = EncodedPoint::from_bytes(&*bytes);
assert!(decode_result.is_err());
}
}
}
#[test]
fn decode_truncated_point() {
for bytes in &[&COMPRESSED_BYTES[..], &UNCOMPRESSED_BYTES[..]] {
for len in 0..bytes.len() {
let decode_result = EncodedPoint::from_bytes(&bytes[..len]);
assert!(decode_result.is_err());
}
}
}
#[test]
fn from_untagged_point() {
let untagged_bytes = hex!("11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222");
let uncompressed_point =
EncodedPoint::from_untagged_bytes(GenericArray::from_slice(&untagged_bytes[..]));
assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]);
}
#[test]
fn from_affine_coordinates() {
let x = hex!("1111111111111111111111111111111111111111111111111111111111111111");
let y = hex!("2222222222222222222222222222222222222222222222222222222222222222");
let uncompressed_point = EncodedPoint::from_affine_coordinates(&x.into(), &y.into(), false);
assert_eq!(uncompressed_point.as_bytes(), &UNCOMPRESSED_BYTES[..]);
let compressed_point = EncodedPoint::from_affine_coordinates(&x.into(), &y.into(), true);
assert_eq!(compressed_point.as_bytes(), &COMPRESSED_BYTES[..]);
}
#[test]
fn compress() {
let uncompressed_point = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap();
let compressed_point = uncompressed_point.compress();
assert_eq!(compressed_point.as_bytes(), &COMPRESSED_BYTES[..]);
}
#[cfg(feature = "subtle")]
#[test]
fn conditional_select() {
let a = EncodedPoint::from_bytes(&COMPRESSED_BYTES[..]).unwrap();
let b = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap();
let a_selected = EncodedPoint::conditional_select(&a, &b, 0.into());
assert_eq!(a, a_selected);
let b_selected = EncodedPoint::conditional_select(&a, &b, 1.into());
assert_eq!(b, b_selected);
}
#[test]
fn identity() {
let identity_point = EncodedPoint::identity();
assert_eq!(identity_point.tag(), Tag::Identity);
assert_eq!(identity_point.len(), 1);
assert_eq!(identity_point.as_bytes(), &IDENTITY_BYTES[..]);
// identity is default
assert_eq!(identity_point, EncodedPoint::default());
}
#[test]
fn decode_hex() {
let point = EncodedPoint::from_str(
"021111111111111111111111111111111111111111111111111111111111111111",
)
.unwrap();
assert_eq!(point.as_bytes(), COMPRESSED_BYTES);
}
#[cfg(feature = "alloc")]
#[test]
fn to_bytes() {
let uncompressed_point = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap();
assert_eq!(&*uncompressed_point.to_bytes(), &UNCOMPRESSED_BYTES[..]);
}
#[cfg(feature = "alloc")]
#[test]
fn to_string() {
let point = EncodedPoint::from_bytes(&COMPRESSED_BYTES[..]).unwrap();
assert_eq!(
point.to_string(),
"021111111111111111111111111111111111111111111111111111111111111111"
);
}
}

177
vendor/sec1/src/private_key.rs vendored Normal file
View File

@@ -0,0 +1,177 @@
//! SEC1 elliptic curve private key support.
//!
//! Support for ASN.1 DER-encoded elliptic curve private keys as described in
//! SEC1: Elliptic Curve Cryptography (Version 2.0) Appendix C.4 (p.108):
//!
//! <https://www.secg.org/sec1-v2.pdf>
use crate::{EcParameters, Error, Result};
use core::fmt;
use der::{
asn1::{BitStringRef, ContextSpecific, ContextSpecificRef, OctetStringRef},
Decode, DecodeValue, Encode, EncodeValue, Header, Length, Reader, Sequence, Tag, TagMode,
TagNumber, Writer,
};
#[cfg(all(feature = "alloc", feature = "zeroize"))]
use der::SecretDocument;
#[cfg(feature = "pem")]
use der::pem::PemLabel;
/// `ECPrivateKey` version.
///
/// From [RFC5913 Section 3]:
/// > version specifies the syntax version number of the elliptic curve
/// > private key structure. For this version of the document, it SHALL
/// > be set to ecPrivkeyVer1, which is of type INTEGER and whose value
/// > is one (1).
///
/// [RFC5915 Section 3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
const VERSION: u8 = 1;
/// Context-specific tag number for the elliptic curve parameters.
const EC_PARAMETERS_TAG: TagNumber = TagNumber::new(0);
/// Context-specific tag number for the public key.
const PUBLIC_KEY_TAG: TagNumber = TagNumber::new(1);
/// SEC1 elliptic curve private key.
///
/// Described in [SEC1: Elliptic Curve Cryptography (Version 2.0)]
/// Appendix C.4 (p.108) and also [RFC5915 Section 3]:
///
/// ```text
/// ECPrivateKey ::= SEQUENCE {
/// version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1),
/// privateKey OCTET STRING,
/// parameters [0] ECParameters {{ NamedCurve }} OPTIONAL,
/// publicKey [1] BIT STRING OPTIONAL
/// }
/// ```
///
/// When encoded as PEM (text), keys in this format begin with the following:
///
/// ```text
/// -----BEGIN EC PRIVATE KEY-----
/// ```
///
/// [SEC1: Elliptic Curve Cryptography (Version 2.0)]: https://www.secg.org/sec1-v2.pdf
/// [RFC5915 Section 3]: https://datatracker.ietf.org/doc/html/rfc5915#section-3
#[derive(Clone)]
pub struct EcPrivateKey<'a> {
/// Private key data.
pub private_key: &'a [u8],
/// Elliptic curve parameters.
pub parameters: Option<EcParameters>,
/// Public key data, optionally available if version is V2.
pub public_key: Option<&'a [u8]>,
}
impl<'a> EcPrivateKey<'a> {
fn context_specific_parameters(&self) -> Option<ContextSpecificRef<'_, EcParameters>> {
self.parameters.as_ref().map(|params| ContextSpecificRef {
tag_number: EC_PARAMETERS_TAG,
tag_mode: TagMode::Explicit,
value: params,
})
}
fn context_specific_public_key(
&self,
) -> der::Result<Option<ContextSpecific<BitStringRef<'a>>>> {
self.public_key
.map(|pk| {
BitStringRef::from_bytes(pk).map(|value| ContextSpecific {
tag_number: PUBLIC_KEY_TAG,
tag_mode: TagMode::Explicit,
value,
})
})
.transpose()
}
}
impl<'a> DecodeValue<'a> for EcPrivateKey<'a> {
fn decode_value<R: Reader<'a>>(reader: &mut R, header: Header) -> der::Result<Self> {
reader.read_nested(header.length, |reader| {
if u8::decode(reader)? != VERSION {
return Err(der::Tag::Integer.value_error());
}
let private_key = OctetStringRef::decode(reader)?.as_bytes();
let parameters = reader.context_specific(EC_PARAMETERS_TAG, TagMode::Explicit)?;
let public_key = reader
.context_specific::<BitStringRef<'_>>(PUBLIC_KEY_TAG, TagMode::Explicit)?
.map(|bs| bs.as_bytes().ok_or_else(|| Tag::BitString.value_error()))
.transpose()?;
Ok(EcPrivateKey {
private_key,
parameters,
public_key,
})
})
}
}
impl EncodeValue for EcPrivateKey<'_> {
fn value_len(&self) -> der::Result<Length> {
VERSION.encoded_len()?
+ OctetStringRef::new(self.private_key)?.encoded_len()?
+ self.context_specific_parameters().encoded_len()?
+ self.context_specific_public_key()?.encoded_len()?
}
fn encode_value(&self, writer: &mut impl Writer) -> der::Result<()> {
VERSION.encode(writer)?;
OctetStringRef::new(self.private_key)?.encode(writer)?;
self.context_specific_parameters().encode(writer)?;
self.context_specific_public_key()?.encode(writer)?;
Ok(())
}
}
impl<'a> Sequence<'a> for EcPrivateKey<'a> {}
impl<'a> TryFrom<&'a [u8]> for EcPrivateKey<'a> {
type Error = Error;
fn try_from(bytes: &'a [u8]) -> Result<EcPrivateKey<'a>> {
Ok(Self::from_der(bytes)?)
}
}
impl<'a> fmt::Debug for EcPrivateKey<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("EcPrivateKey")
.field("parameters", &self.parameters)
.field("public_key", &self.public_key)
.finish_non_exhaustive()
}
}
#[cfg(feature = "alloc")]
impl TryFrom<EcPrivateKey<'_>> for SecretDocument {
type Error = Error;
fn try_from(private_key: EcPrivateKey<'_>) -> Result<Self> {
SecretDocument::try_from(&private_key)
}
}
#[cfg(feature = "alloc")]
impl TryFrom<&EcPrivateKey<'_>> for SecretDocument {
type Error = Error;
fn try_from(private_key: &EcPrivateKey<'_>) -> Result<Self> {
Ok(Self::encode_msg(private_key)?)
}
}
#[cfg(feature = "pem")]
impl PemLabel for EcPrivateKey<'_> {
const PEM_LABEL: &'static str = "EC PRIVATE KEY";
}

122
vendor/sec1/src/traits.rs vendored Normal file
View File

@@ -0,0 +1,122 @@
//! Traits for parsing objects from SEC1 encoded documents
use crate::Result;
#[cfg(feature = "alloc")]
use der::SecretDocument;
#[cfg(feature = "pem")]
use {crate::LineEnding, alloc::string::String, der::pem::PemLabel};
#[cfg(feature = "pkcs8")]
use {
crate::{EcPrivateKey, ALGORITHM_OID},
der::Decode,
};
#[cfg(feature = "std")]
use std::path::Path;
#[cfg(feature = "pem")]
use zeroize::Zeroizing;
/// Parse an [`EcPrivateKey`] from a SEC1-encoded document.
pub trait DecodeEcPrivateKey: Sized {
/// Deserialize SEC1 private key from ASN.1 DER-encoded data
/// (binary format).
fn from_sec1_der(bytes: &[u8]) -> Result<Self>;
/// Deserialize SEC1-encoded private key from PEM.
///
/// Keys in this format begin with the following:
///
/// ```text
/// -----BEGIN EC PRIVATE KEY-----
/// ```
#[cfg(feature = "pem")]
fn from_sec1_pem(s: &str) -> Result<Self> {
let (label, doc) = SecretDocument::from_pem(s)?;
EcPrivateKey::validate_pem_label(label)?;
Self::from_sec1_der(doc.as_bytes())
}
/// Load SEC1 private key from an ASN.1 DER-encoded file on the local
/// filesystem (binary format).
#[cfg(feature = "std")]
fn read_sec1_der_file(path: impl AsRef<Path>) -> Result<Self> {
Self::from_sec1_der(SecretDocument::read_der_file(path)?.as_bytes())
}
/// Load SEC1 private key from a PEM-encoded file on the local filesystem.
#[cfg(all(feature = "pem", feature = "std"))]
fn read_sec1_pem_file(path: impl AsRef<Path>) -> Result<Self> {
let (label, doc) = SecretDocument::read_pem_file(path)?;
EcPrivateKey::validate_pem_label(&label)?;
Self::from_sec1_der(doc.as_bytes())
}
}
/// Serialize a [`EcPrivateKey`] to a SEC1 encoded document.
#[cfg(feature = "alloc")]
pub trait EncodeEcPrivateKey {
/// Serialize a [`SecretDocument`] containing a SEC1-encoded private key.
fn to_sec1_der(&self) -> Result<SecretDocument>;
/// Serialize this private key as PEM-encoded SEC1 with the given [`LineEnding`].
///
/// To use the OS's native line endings, pass `Default::default()`.
#[cfg(feature = "pem")]
fn to_sec1_pem(&self, line_ending: LineEnding) -> Result<Zeroizing<String>> {
let doc = self.to_sec1_der()?;
Ok(doc.to_pem(EcPrivateKey::PEM_LABEL, line_ending)?)
}
/// Write ASN.1 DER-encoded SEC1 private key to the given path.
#[cfg(feature = "std")]
fn write_sec1_der_file(&self, path: impl AsRef<Path>) -> Result<()> {
Ok(self.to_sec1_der()?.write_der_file(path)?)
}
/// Write ASN.1 DER-encoded SEC1 private key to the given path.
#[cfg(all(feature = "pem", feature = "std"))]
fn write_sec1_pem_file(&self, path: impl AsRef<Path>, line_ending: LineEnding) -> Result<()> {
let doc = self.to_sec1_der()?;
Ok(doc.write_pem_file(path, EcPrivateKey::PEM_LABEL, line_ending)?)
}
}
#[cfg(feature = "pkcs8")]
impl<T> DecodeEcPrivateKey for T
where
T: for<'a> TryFrom<pkcs8::PrivateKeyInfo<'a>, Error = pkcs8::Error>,
{
fn from_sec1_der(private_key: &[u8]) -> Result<Self> {
let params_oid = EcPrivateKey::from_der(private_key)?
.parameters
.and_then(|params| params.named_curve());
let algorithm = pkcs8::AlgorithmIdentifierRef {
oid: ALGORITHM_OID,
parameters: params_oid.as_ref().map(Into::into),
};
Ok(Self::try_from(pkcs8::PrivateKeyInfo {
algorithm,
private_key,
public_key: None,
})?)
}
}
#[cfg(all(feature = "alloc", feature = "pkcs8"))]
impl<T: pkcs8::EncodePrivateKey> EncodeEcPrivateKey for T {
fn to_sec1_der(&self) -> Result<SecretDocument> {
let doc = self.to_pkcs8_der()?;
let pkcs8_key = pkcs8::PrivateKeyInfo::from_der(doc.as_bytes())?;
pkcs8_key.algorithm.assert_algorithm_oid(ALGORITHM_OID)?;
let mut pkcs1_key = EcPrivateKey::from_der(pkcs8_key.private_key)?;
pkcs1_key.parameters = Some(pkcs8_key.algorithm.parameters_oid()?.into());
pkcs1_key.try_into()
}
}