From db135939d2704ecc45feef0e75500b023391f795 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 19 Sep 2021 12:19:27 -0600 Subject: [PATCH] elliptic-curve: use `sec1` crate's `EncodedPoint` The `EncodedPoint` type was extracted to the `sec1` crate in: https://github.com/RustCrypto/formats/pull/45 This switches the `elliptic-curve` to using the newly extracted implementation. This was a bit tricky/invasive as now `EncodedPoint` now only knows a size and not the rest of the details of the curve. APIs that were previously heavily leveraging `EncodedPoint` should probably switch to the `PublicKey` type instead unless they are specifically intending to work with SEC1-encoded points. --- .github/workflows/elliptic-curve.yml | 3 +- Cargo.lock | 7 +- elliptic-curve/src/arithmetic.rs | 2 + elliptic-curve/src/error.rs | 2 +- elliptic-curve/src/jwk.rs | 191 +++---- elliptic-curve/src/lib.rs | 2 + elliptic-curve/src/public_key.rs | 167 ++---- elliptic-curve/src/sec1.rs | 30 +- elliptic-curve/src/sec1/encoded_point.rs | 669 ----------------------- elliptic-curve/src/secret_key.rs | 37 +- elliptic-curve/src/secret_key/pkcs8.rs | 16 +- 11 files changed, 158 insertions(+), 968 deletions(-) delete mode 100644 elliptic-curve/src/sec1/encoded_point.rs diff --git a/.github/workflows/elliptic-curve.yml b/.github/workflows/elliptic-curve.yml index 9fbd2fc33..a13f67280 100644 --- a/.github/workflows/elliptic-curve.yml +++ b/.github/workflows/elliptic-curve.yml @@ -46,7 +46,8 @@ jobs: - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features pkcs8 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features sec1 - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features pkcs8,sec1 - - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features ecdh,hazmat,jwk,pem,sec1 + - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features pem,pkcs8,sec1 + - run: cargo build --target ${{ matrix.target }} --release --no-default-features --features ecdh,hazmat,jwk,pem,pkcs8,sec1 test: runs-on: ubuntu-latest diff --git a/Cargo.lock b/Cargo.lock index a8d751281..4724d08f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -118,7 +118,7 @@ dependencies = [ [[package]] name = "const-oid" version = "0.6.1" -source = "git+https://github.com/rustcrypto/formats#46a02c5e43a213e20e07af1e6bc19512c6a5fd97" +source = "git+https://github.com/rustcrypto/formats#77564c368c60486dbbdfb6e8ab6b1ec9f79aba62" [[package]] name = "cpufeatures" @@ -188,7 +188,7 @@ dependencies = [ [[package]] name = "der" version = "0.4.3" -source = "git+https://github.com/rustcrypto/formats#46a02c5e43a213e20e07af1e6bc19512c6a5fd97" +source = "git+https://github.com/rustcrypto/formats#77564c368c60486dbbdfb6e8ab6b1ec9f79aba62" dependencies = [ "const-oid", ] @@ -396,9 +396,10 @@ checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" [[package]] name = "sec1" version = "0.0.0" -source = "git+https://github.com/rustcrypto/formats#46a02c5e43a213e20e07af1e6bc19512c6a5fd97" +source = "git+https://github.com/rustcrypto/formats#77564c368c60486dbbdfb6e8ab6b1ec9f79aba62" dependencies = [ "der", + "generic-array", "zeroize", ] diff --git a/elliptic-curve/src/arithmetic.rs b/elliptic-curve/src/arithmetic.rs index b43c3b8a2..341982c14 100644 --- a/elliptic-curve/src/arithmetic.rs +++ b/elliptic-curve/src/arithmetic.rs @@ -17,6 +17,8 @@ pub trait AffineArithmetic: Curve + ScalarArithmetic { + Debug + Default + DefaultIsZeroes + + Eq + + PartialEq + Sized + Send + Sync; diff --git a/elliptic-curve/src/error.rs b/elliptic-curve/src/error.rs index e579601f0..916295603 100644 --- a/elliptic-curve/src/error.rs +++ b/elliptic-curve/src/error.rs @@ -2,7 +2,7 @@ use core::fmt::{self, Display}; -/// Result type. +/// Result type with the `elliptic-curve` crate's [`Error`] type. pub type Result = core::result::Result; /// Elliptic curve errors. diff --git a/elliptic-curve/src/jwk.rs b/elliptic-curve/src/jwk.rs index 6d10e843f..ba4827cda 100644 --- a/elliptic-curve/src/jwk.rs +++ b/elliptic-curve/src/jwk.rs @@ -4,11 +4,9 @@ //! use crate::{ - sec1::{ - Coordinates, EncodedPoint, UncompressedPointSize, UntaggedPointSize, ValidatePublicKey, - }, + sec1::{Coordinates, EncodedPoint, ModulusSize, ValidatePublicKey}, secret_key::SecretKey, - Curve, Error, FieldBytes, PrimeCurve, + Curve, Error, FieldBytes, FieldSize, PrimeCurve, Result, }; use alloc::{ borrow::ToOwned, @@ -20,10 +18,8 @@ use core::{ convert::{TryFrom, TryInto}, fmt::{self, Debug}, marker::PhantomData, - ops::Add, str::{self, FromStr}, }; -use generic_array::{typenum::U1, ArrayLength}; use serde::{de, ser, Deserialize, Serialize}; use zeroize::Zeroize; @@ -116,24 +112,54 @@ impl JwkEcKey { /// Decode a JWK into a [`PublicKey`]. #[cfg(feature = "arithmetic")] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] - pub fn to_public_key(&self) -> Result, Error> + pub fn to_public_key(&self) -> Result> where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { - self.try_into() + PublicKey::from_sec1_bytes(self.to_encoded_point::()?.as_bytes()) + } + + /// Create a JWK from a SEC1 [`EncodedPoint`]. + pub fn from_encoded_point(point: &EncodedPoint) -> Option + where + C: PrimeCurve + JwkParameters, + FieldSize: ModulusSize, + { + match point.coordinates() { + Coordinates::Uncompressed { x, y } => Some(JwkEcKey { + crv: C::CRV.to_owned(), + x: Base64Url::encode_string(x), + y: Base64Url::encode_string(y), + d: None, + }), + _ => None, + } + } + + /// Get the public key component of this JWK as a SEC1 [`EncodedPoint`]. + pub fn to_encoded_point(&self) -> Result> + where + C: PrimeCurve + JwkParameters, + FieldSize: ModulusSize, + { + if self.crv != C::CRV { + return Err(Error); + } + + let x = decode_base64url_fe::(&self.x)?; + let y = decode_base64url_fe::(&self.y)?; + Ok(EncodedPoint::::from_affine_coordinates(&x, &y, false)) } /// Decode a JWK into a [`SecretKey`]. #[cfg(feature = "arithmetic")] #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] - pub fn to_secret_key(&self) -> Result, Error> + pub fn to_secret_key(&self) -> Result> where C: PrimeCurve + JwkParameters + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { self.try_into() } @@ -142,7 +168,7 @@ impl JwkEcKey { impl FromStr for JwkEcKey { type Err = Error; - fn from_str(s: &str) -> Result { + fn from_str(s: &str) -> Result { serde_json::from_str(s).map_err(|_| Error) } } @@ -153,73 +179,16 @@ impl ToString for JwkEcKey { } } -#[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] -impl TryFrom for EncodedPoint -where - C: PrimeCurve + JwkParameters, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - type Error = Error; - - fn try_from(jwk: JwkEcKey) -> Result, Error> { - (&jwk).try_into() - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] -impl TryFrom<&JwkEcKey> for EncodedPoint -where - C: PrimeCurve + JwkParameters, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - type Error = Error; - - fn try_from(jwk: &JwkEcKey) -> Result, Error> { - if jwk.crv != C::CRV { - return Err(Error); - } - - let x = decode_base64url_fe::(&jwk.x)?; - let y = decode_base64url_fe::(&jwk.y)?; - Ok(EncodedPoint::from_affine_coordinates(&x, &y, false)) - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] -impl TryFrom> for JwkEcKey -where - C: PrimeCurve + JwkParameters, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - type Error = Error; - - fn try_from(point: EncodedPoint) -> Result { - (&point).try_into() - } -} - -#[cfg_attr(docsrs, doc(cfg(feature = "jwk")))] -impl TryFrom<&EncodedPoint> for JwkEcKey +#[cfg(feature = "arithmetic")] +#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] +impl FromEncodedPoint for JwkEcKey where - C: PrimeCurve + JwkParameters, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + C: PrimeCurve + JwkParameters + ProjectiveArithmetic, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: ModulusSize, { - type Error = Error; - - fn try_from(point: &EncodedPoint) -> Result { - match point.coordinates() { - Coordinates::Uncompressed { x, y } => Ok(JwkEcKey { - crv: C::CRV.to_owned(), - x: Base64Url::encode_string(x), - y: Base64Url::encode_string(y), - d: None, - }), - _ => Err(Error), - } + fn from_encoded_point(point: &EncodedPoint) -> Option { + Self::from_encoded_point::(point) } } @@ -227,12 +196,11 @@ where impl TryFrom for SecretKey where C: PrimeCurve + JwkParameters + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { type Error = Error; - fn try_from(jwk: JwkEcKey) -> Result, Error> { + fn try_from(jwk: JwkEcKey) -> Result> { (&jwk).try_into() } } @@ -241,14 +209,13 @@ where impl TryFrom<&JwkEcKey> for SecretKey where C: PrimeCurve + JwkParameters + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { type Error = Error; - fn try_from(jwk: &JwkEcKey) -> Result, Error> { + fn try_from(jwk: &JwkEcKey) -> Result> { if let Some(d_base64) = &jwk.d { - let pk = EncodedPoint::::try_from(jwk)?; + let pk = jwk.to_encoded_point::()?; let mut d_bytes = decode_base64url_fe::(d_base64)?; let result = SecretKey::from_be_bytes(&d_bytes); d_bytes.zeroize(); @@ -270,8 +237,7 @@ impl From> for JwkEcKey where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn from(sk: SecretKey) -> JwkEcKey { (&sk).into() @@ -285,8 +251,7 @@ impl From<&SecretKey> for JwkEcKey where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn from(sk: &SecretKey) -> JwkEcKey { let mut jwk = sk.public_key().to_jwk(); @@ -304,12 +269,11 @@ impl TryFrom for PublicKey where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { type Error = Error; - fn try_from(jwk: JwkEcKey) -> Result, Error> { + fn try_from(jwk: JwkEcKey) -> Result> { (&jwk).try_into() } } @@ -321,13 +285,12 @@ impl TryFrom<&JwkEcKey> for PublicKey where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { type Error = Error; - fn try_from(jwk: &JwkEcKey) -> Result, Error> { - EncodedPoint::::try_from(jwk).and_then(PublicKey::try_from) + fn try_from(jwk: &JwkEcKey) -> Result> { + PublicKey::from_sec1_bytes(jwk.to_encoded_point::()?.as_bytes()) } } @@ -338,8 +301,7 @@ impl From> for JwkEcKey where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn from(pk: PublicKey) -> JwkEcKey { (&pk).into() @@ -353,13 +315,10 @@ impl From<&PublicKey> for JwkEcKey where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn from(pk: &PublicKey) -> JwkEcKey { - pk.to_encoded_point(false) - .try_into() - .expect("JWK encoding error") + Self::from_encoded_point::(&pk.to_encoded_point(false)).expect("JWK encoding error") } } @@ -415,7 +374,7 @@ impl Zeroize for JwkEcKey { } impl<'de> Deserialize<'de> for JwkEcKey { - fn deserialize(deserializer: D) -> Result + fn deserialize(deserializer: D) -> core::result::Result where D: de::Deserializer<'de>, { @@ -438,7 +397,7 @@ impl<'de> Deserialize<'de> for JwkEcKey { fmt::Formatter::write_str(formatter, "field identifier") } - fn visit_u64(self, value: u64) -> Result + fn visit_u64(self, value: u64) -> core::result::Result where E: de::Error, { @@ -455,14 +414,14 @@ impl<'de> Deserialize<'de> for JwkEcKey { } } - fn visit_str(self, value: &str) -> Result + fn visit_str(self, value: &str) -> core::result::Result where E: de::Error, { self.visit_bytes(value.as_bytes()) } - fn visit_bytes(self, value: &[u8]) -> Result + fn visit_bytes(self, value: &[u8]) -> core::result::Result where E: de::Error, { @@ -482,7 +441,7 @@ impl<'de> Deserialize<'de> for JwkEcKey { impl<'de> Deserialize<'de> for Field { #[inline] - fn deserialize(__deserializer: D) -> Result + fn deserialize(__deserializer: D) -> core::result::Result where D: de::Deserializer<'de>, { @@ -503,7 +462,7 @@ impl<'de> Deserialize<'de> for JwkEcKey { } #[inline] - fn visit_seq(self, mut seq: A) -> Result + fn visit_seq(self, mut seq: A) -> core::result::Result where A: de::SeqAccess<'de>, { @@ -530,7 +489,7 @@ impl<'de> Deserialize<'de> for JwkEcKey { } #[inline] - fn visit_map(self, mut map: A) -> Result + fn visit_map(self, mut map: A) -> core::result::Result where A: de::MapAccess<'de>, { @@ -607,7 +566,7 @@ impl<'de> Deserialize<'de> for JwkEcKey { } impl Serialize for JwkEcKey { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: S) -> core::result::Result where S: ser::Serializer, { @@ -628,7 +587,7 @@ impl Serialize for JwkEcKey { } /// Decode a Base64url-encoded field element -fn decode_base64url_fe(s: &str) -> Result, Error> { +fn decode_base64url_fe(s: &str) -> Result> { let mut result = FieldBytes::::default(); Base64Url::decode(s, &mut result).map_err(|_| Error)?; Ok(result) @@ -724,7 +683,7 @@ mod tests { #[test] fn jwk_into_encoded_point() { let jwk = JwkEcKey::from_str(JWK_PUBLIC_KEY).unwrap(); - let point = EncodedPoint::::try_from(&jwk).unwrap(); + let point = jwk.to_encoded_point::().unwrap(); let (x, y) = match point.coordinates() { Coordinates::Uncompressed { x, y } => (x, y), other => panic!("unexpected coordinates: {:?}", other), @@ -738,8 +697,8 @@ mod tests { #[test] fn encoded_point_into_jwk() { let jwk = JwkEcKey::from_str(JWK_PUBLIC_KEY).unwrap(); - let point = EncodedPoint::::try_from(&jwk).unwrap(); - let jwk2 = JwkEcKey::try_from(point).unwrap(); + let point = jwk.to_encoded_point::().unwrap(); + let jwk2 = JwkEcKey::from_encoded_point::(&point).unwrap(); assert_eq!(jwk, jwk2); } } diff --git a/elliptic-curve/src/lib.rs b/elliptic-curve/src/lib.rs index 4d6441c32..345ab5603 100644 --- a/elliptic-curve/src/lib.rs +++ b/elliptic-curve/src/lib.rs @@ -32,6 +32,8 @@ extern crate std; pub use rand_core; pub mod ops; + +#[cfg(feature = "sec1")] pub mod sec1; mod error; diff --git a/elliptic-curve/src/public_key.rs b/elliptic-curve/src/public_key.rs index 11ec37642..72e9f42a3 100644 --- a/elliptic-curve/src/public_key.rs +++ b/elliptic-curve/src/public_key.rs @@ -1,27 +1,15 @@ //! Elliptic curve public keys. use crate::{ - consts::U1, - point::PointCompression, - sec1::{ - EncodedPoint, FromEncodedPoint, ToEncodedPoint, UncompressedPointSize, UntaggedPointSize, - }, - AffinePoint, Curve, Error, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, ProjectivePoint, - Result, -}; -use core::{ - cmp::Ordering, - convert::{TryFrom, TryInto}, - fmt::Debug, - ops::Add, + AffinePoint, Curve, Error, NonZeroScalar, ProjectiveArithmetic, ProjectivePoint, Result, }; -use generic_array::ArrayLength; +use core::fmt::Debug; use group::{Curve as _, Group}; #[cfg(feature = "jwk")] use crate::{JwkEcKey, JwkParameters}; -#[cfg(feature = "pkcs8")] +#[cfg(all(feature = "sec1", feature = "pkcs8"))] use { crate::{AlgorithmParameters, ALGORITHM_OID}, pkcs8::FromPublicKey, @@ -30,6 +18,15 @@ use { #[cfg(feature = "pem")] use {core::str::FromStr, pkcs8::ToPublicKey}; +#[cfg(feature = "sec1")] +use { + crate::{ + sec1::{EncodedPoint, FromEncodedPoint, ModulusSize, ToEncodedPoint}, + FieldSize, PrimeCurve, + }, + core::cmp::Ordering, +}; + #[cfg(any(feature = "jwk", feature = "pem"))] use alloc::string::{String, ToString}; @@ -60,7 +57,7 @@ use alloc::string::{String, ToString}; /// When the `pem` feature of this crate (or a specific RustCrypto elliptic /// curve crate) is enabled, a [`FromStr`] impl is also available. #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] -#[derive(Clone, Debug)] +#[derive(Clone, Debug, Eq, PartialEq)] pub struct PublicKey where C: Curve + ProjectiveArithmetic, @@ -96,16 +93,15 @@ where /// 2.3.3 (page 10). /// /// + #[cfg(feature = "sec1")] pub fn from_sec1_bytes(bytes: &[u8]) -> Result where - Self: TryFrom, Error = Error>, C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, + AffinePoint: FromEncodedPoint + ToEncodedPoint, { - EncodedPoint::from_bytes(bytes) - .map_err(|_| Error) - .and_then(TryInto::try_into) + let point = EncodedPoint::::from_bytes(bytes).map_err(|_| Error)?; + Self::from_encoded_point(&point).ok_or(Error) } /// Borrow the inner [`AffinePoint`] from this [`PublicKey`]. @@ -127,10 +123,9 @@ where where C: PrimeCurve + JwkParameters, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { - jwk.try_into() + jwk.to_public_key::() } /// Parse a string containing a JSON Web Key (JWK) into a [`PublicKey`]. @@ -140,8 +135,7 @@ where where C: PrimeCurve + JwkParameters, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { jwk.parse::().and_then(|jwk| Self::from_jwk(&jwk)) } @@ -153,8 +147,7 @@ where where C: PrimeCurve + JwkParameters, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { self.into() } @@ -166,8 +159,7 @@ where where C: PrimeCurve + JwkParameters, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { self.to_jwk().to_string() } @@ -184,64 +176,13 @@ where impl Copy for PublicKey where C: Curve + ProjectiveArithmetic {} -impl TryFrom> for PublicKey -where - C: PrimeCurve + ProjectiveArithmetic, - AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - type Error = Error; - - fn try_from(encoded_point: EncodedPoint) -> Result { - encoded_point.decode() - } -} - -impl TryFrom<&EncodedPoint> for PublicKey -where - C: PrimeCurve + ProjectiveArithmetic, - AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - type Error = Error; - - fn try_from(encoded_point: &EncodedPoint) -> Result { - encoded_point.decode() - } -} - -impl From> for EncodedPoint -where - C: PrimeCurve + ProjectiveArithmetic + PointCompression, - AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - fn from(public_key: PublicKey) -> EncodedPoint { - EncodedPoint::::from(&public_key) - } -} - -impl From<&PublicKey> for EncodedPoint -where - C: PrimeCurve + ProjectiveArithmetic + PointCompression, - AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - fn from(public_key: &PublicKey) -> EncodedPoint { - public_key.to_encoded_point(C::COMPRESS_POINTS) - } -} - +#[cfg(feature = "sec1")] +#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))] impl FromEncodedPoint for PublicKey where C: PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { /// Initialize [`PublicKey`] from an [`EncodedPoint`] fn from_encoded_point(encoded_point: &EncodedPoint) -> Option { @@ -250,12 +191,13 @@ where } } +#[cfg(feature = "sec1")] +#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))] impl ToEncodedPoint for PublicKey where C: PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { /// Serialize this [`PublicKey`] as a SEC1 [`EncodedPoint`], optionally applying /// point compression @@ -264,45 +206,26 @@ where } } -impl Eq for PublicKey -where - C: PrimeCurve + ProjectiveArithmetic, - AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ -} - -impl PartialEq for PublicKey -where - C: PrimeCurve + ProjectiveArithmetic, - AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - fn eq(&self, other: &Self) -> bool { - self.cmp(other) == Ordering::Equal - } -} - +#[cfg(feature = "sec1")] +#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))] impl PartialOrd for PublicKey where C: PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } +#[cfg(feature = "sec1")] +#[cfg_attr(docsrs, doc(cfg(feature = "sec1")))] impl Ord for PublicKey where C: PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn cmp(&self, other: &Self) -> Ordering { // TODO(tarcieri): more efficient implementation? @@ -312,14 +235,13 @@ where } } -#[cfg(feature = "pkcs8")] -#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] +#[cfg(all(feature = "pkcs8", feature = "sec1"))] +#[cfg_attr(docsrs, doc(cfg(all(feature = "pkcs8", feature = "sec1"))))] impl FromPublicKey for PublicKey where - Self: TryFrom, Error = Error>, C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: ModulusSize, { fn from_spki(spki: pkcs8::SubjectPublicKeyInfo<'_>) -> pkcs8::Result { if spki.algorithm.oid != ALGORITHM_OID { @@ -346,8 +268,7 @@ impl ToPublicKey for PublicKey where C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn to_public_key_der(&self) -> pkcs8::Result { let public_key_bytes = self.to_encoded_point(false); @@ -364,10 +285,9 @@ where #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl FromStr for PublicKey where - Self: TryFrom, Error = Error>, C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + AffinePoint: FromEncodedPoint + ToEncodedPoint, + FieldSize: ModulusSize, { type Err = Error; @@ -382,8 +302,7 @@ impl ToString for PublicKey where C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn to_string(&self) -> String { self.to_public_key_pem().expect("PEM encoding error") diff --git a/elliptic-curve/src/sec1.rs b/elliptic-curve/src/sec1.rs index 81ec9cd3b..a0abd5f0d 100644 --- a/elliptic-curve/src/sec1.rs +++ b/elliptic-curve/src/sec1.rs @@ -2,19 +2,16 @@ //! //! -mod encoded_point; +pub use sec1::point::{Coordinates, ModulusSize, Tag}; -pub use self::encoded_point::{ - CompressedPointSize, Coordinates, EncodedPoint, Tag, UncompressedPointSize, UntaggedPointSize, -}; - -use crate::{PrimeCurve, Result, SecretKey}; -use core::ops::Add; -use generic_array::{typenum::U1, ArrayLength}; +use crate::{FieldSize, PrimeCurve, Result, SecretKey}; #[cfg(feature = "arithmetic")] use crate::{AffinePoint, Error, ProjectiveArithmetic}; +/// Encoded elliptic curve point sized appropriately for a given curve. +pub type EncodedPoint = sec1::point::EncodedPoint>; + /// Trait for deserializing a value from a SEC1 encoded curve point. /// /// This is intended for use with the `AffinePoint` type for a given elliptic curve. @@ -22,15 +19,14 @@ pub trait FromEncodedPoint where Self: Sized, C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { /// Deserialize the type this trait is impl'd on from an [`EncodedPoint`]. /// /// # Returns /// /// `None` if the [`EncodedPoint`] is invalid. - fn from_encoded_point(public_key: &EncodedPoint) -> Option; + fn from_encoded_point(point: &EncodedPoint) -> Option; } /// Trait for serializing a value to a SEC1 encoded curve point. @@ -39,8 +35,7 @@ where pub trait ToEncodedPoint where C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { /// Serialize this value as a SEC1 [`EncodedPoint`], optionally applying /// point compression. @@ -53,8 +48,7 @@ where pub trait ToCompactEncodedPoint where C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { /// Serialize this value as a SEC1 [`EncodedPoint`], optionally applying /// point compression. @@ -69,8 +63,7 @@ where pub trait ValidatePublicKey where Self: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { /// Validate that the given [`EncodedPoint`] is a valid public key for the /// provided secret value. @@ -95,8 +88,7 @@ impl ValidatePublicKey for C where C: PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn validate_public_key(secret_key: &SecretKey, public_key: &EncodedPoint) -> Result<()> { let pk = secret_key diff --git a/elliptic-curve/src/sec1/encoded_point.rs b/elliptic-curve/src/sec1/encoded_point.rs deleted file mode 100644 index 28a086ff1..000000000 --- a/elliptic-curve/src/sec1/encoded_point.rs +++ /dev/null @@ -1,669 +0,0 @@ -//! SEC1 encoded point support. -//! -//! Support for the `Elliptic-Curve-Point-to-Octet-String` encoding described -//! in SEC1: Elliptic Curve Cryptography (Version 2.0) section 2.3.3 (p.10): -//! -//! - -use super::{FromEncodedPoint, ToEncodedPoint}; -use crate::{bigint::Encoding as _, Error, FieldBytes, FieldSize, PrimeCurve, Result}; -use core::{ - fmt::{self, Debug}, - ops::Add, -}; -use generic_array::{typenum::U1, ArrayLength, GenericArray}; -use subtle::{Choice, ConditionallySelectable}; -use zeroize::Zeroize; - -#[cfg(feature = "alloc")] -use alloc::boxed::Box; - -#[cfg(feature = "arithmetic")] -use crate::{AffinePoint, DecompressPoint, ProjectiveArithmetic, SecretKey}; - -#[cfg(all(feature = "arithmetic"))] -use crate::group::{Curve as _, Group}; - -/// 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). -pub type CompressedPointSize = as Add>::Output; - -/// 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). -pub type UncompressedPointSize = as Add>::Output; - -/// Size of an untagged point for given elliptic curve. -pub type UntaggedPointSize = as Add>::Output; - -/// 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, Eq, PartialEq, PartialOrd, Ord)] -pub struct EncodedPoint -where - C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - bytes: GenericArray>, -} - -#[allow(clippy::len_without_is_empty)] -impl EncodedPoint -where - C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - /// 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). - /// - /// - pub fn from_bytes(input: impl AsRef<[u8]>) -> Result { - let input = input.as_ref(); - - // Validate tag - let tag = input.first().cloned().ok_or(Error).and_then(Tag::from_u8)?; - - // Validate length - let expected_len = tag.message_len(C::UInt::BYTE_SIZE); - - if input.len() != expected_len { - return Err(Error); - } - - 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>) -> Self { - let (x, y) = bytes.split_at(C::UInt::BYTE_SIZE); - 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: &FieldBytes, y: &FieldBytes, 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..(C::UInt::BYTE_SIZE + 1)].copy_from_slice(x); - - if !compress { - bytes[(C::UInt::BYTE_SIZE + 1)..].copy_from_slice(y); - } - - Self { bytes } - } - - /// Compute [`EncodedPoint`] representing the public key for the provided - /// [`SecretKey`]. - /// - /// The `compress` flag requests point compression. - #[cfg(all(feature = "arithmetic"))] - #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] - pub fn from_secret_key(secret_key: &SecretKey, compress: bool) -> Self - where - C: PrimeCurve + ProjectiveArithmetic, - AffinePoint: ToEncodedPoint, - { - (C::ProjectivePoint::generator() * secret_key.to_nonzero_scalar().as_ref()) - .to_affine() - .to_encoded_point(compress) - } - - /// 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(C::UInt::BYTE_SIZE) - } - - /// 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")] - #[cfg_attr(docsrs, doc(cfg(feature = "alloc")))] - pub fn to_bytes(&self) -> Box<[u8]> { - self.as_bytes().to_vec().into_boxed_slice() - } - - /// Serialize point as raw uncompressed coordinates without tag byte, i.e. - /// encoded as the concatenated `x || y` coordinates. - #[cfg(feature = "arithmetic")] - #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] - pub fn to_untagged_bytes(&self) -> Option>> - where - C: PrimeCurve + ProjectiveArithmetic, - AffinePoint: DecompressPoint + ToEncodedPoint, - { - self.decompress().map(|point| { - let mut bytes = GenericArray::>::default(); - bytes.copy_from_slice(&point.as_bytes()[1..]); - bytes - }) - } - - /// 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), - } - } - - /// Decompress this [`EncodedPoint`], returning a new [`EncodedPoint`]. - #[cfg(feature = "arithmetic")] - #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] - pub fn decompress(&self) -> Option - where - C: PrimeCurve + ProjectiveArithmetic, - AffinePoint: DecompressPoint + ToEncodedPoint, - { - match self.coordinates() { - Coordinates::Compressed { x, y_is_odd } => { - AffinePoint::::decompress(x, Choice::from(y_is_odd as u8)) - .map(|s| s.to_encoded_point(false)) - .into() - } - Coordinates::Compact { .. } | Coordinates::Identity => None, - Coordinates::Uncompressed { .. } => Some(self.clone()), - } - } - - /// Encode an [`EncodedPoint`] from the desired type - pub fn encode(encodable: T, compress: bool) -> Self - where - T: ToEncodedPoint, - { - encodable.to_encoded_point(compress) - } - - /// Decode this [`EncodedPoint`] into the desired type - pub fn decode(&self) -> Result - where - T: FromEncodedPoint, - { - T::from_encoded_point(self).ok_or(Error) - } - - /// 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<'_, C> { - if self.is_identity() { - return Coordinates::Identity; - } - - let (x, y) = self.bytes[1..].split_at(C::UInt::BYTE_SIZE); - - 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<&FieldBytes> { - 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<&FieldBytes> { - match self.coordinates() { - Coordinates::Compressed { .. } | Coordinates::Identity => None, - Coordinates::Uncompressed { y, .. } => Some(y), - Coordinates::Compact { .. } => None, - } - } -} - -impl AsRef<[u8]> for EncodedPoint -where - C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - #[inline] - fn as_ref(&self) -> &[u8] { - self.as_bytes() - } -} - -impl ConditionallySelectable for EncodedPoint -where - C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, - as ArrayLength>::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 Copy for EncodedPoint -where - C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, - as ArrayLength>::ArrayType: Copy, -{ -} - -impl Debug for EncodedPoint -where - C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "EncodedPoint<{:?}>({:?})", C::default(), &self.bytes) - } -} - -impl Zeroize for EncodedPoint -where - C: PrimeCurve, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, -{ - fn zeroize(&mut self) { - self.bytes.zeroize(); - *self = Self::identity(); - } -} - -/// Enum representing the coordinates of either compressed or uncompressed -/// SEC1-encoded elliptic curve points. -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum Coordinates<'a, C: PrimeCurve> { - /// Identity point (a.k.a. point at infinity) - Identity, - - /// Compact curve point - Compact { - /// x-coordinate - x: &'a FieldBytes, - }, - - /// Compressed curve point - Compressed { - /// x-coordinate - x: &'a FieldBytes, - - /// Is the y-coordinate odd? - y_is_odd: bool, - }, - - /// Uncompressed curve point - Uncompressed { - /// x-coordinate - x: &'a FieldBytes, - - /// y-coordinate - y: &'a FieldBytes, - }, -} - -impl<'a, C: PrimeCurve> Coordinates<'a, C> { - /// 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 { - match byte { - 0 => Ok(Tag::Identity), - 2 => Ok(Tag::CompressedEvenY), - 3 => Ok(Tag::CompressedOddY), - 4 => Ok(Tag::Uncompressed), - 5 => Ok(Tag::Compact), - _ => Err(Error), - } - } - - /// 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 From for u8 { - fn from(tag: Tag) -> u8 { - tag as u8 - } -} - -#[cfg(all(test, feature = "dev"))] -mod tests { - use super::{Coordinates, Tag}; - use crate::dev::MockCurve; - use generic_array::GenericArray; - use hex_literal::hex; - use subtle::ConditionallySelectable; - - type EncodedPoint = super::EncodedPoint; - - /// 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.clone(); - let mut uncompressed_bytes = UNCOMPRESSED_BYTES.clone(); - - 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[..]); - } - - #[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()); - } - - #[cfg(feature = "alloc")] - #[test] - fn to_bytes() { - let uncompressed_point = EncodedPoint::from_bytes(&UNCOMPRESSED_BYTES[..]).unwrap(); - assert_eq!(&*uncompressed_point.to_bytes(), &UNCOMPRESSED_BYTES[..]); - } -} diff --git a/elliptic-curve/src/secret_key.rs b/elliptic-curve/src/secret_key.rs index 947f62fe9..ae38c8d9d 100644 --- a/elliptic-curve/src/secret_key.rs +++ b/elliptic-curve/src/secret_key.rs @@ -49,14 +49,10 @@ use pem_rfc7468 as pem; #[cfg(feature = "sec1")] use { crate::{ - sec1::{EncodedPoint, UncompressedPointSize, UntaggedPointSize, ValidatePublicKey}, - PrimeCurve, + sec1::{EncodedPoint, ModulusSize, ValidatePublicKey}, + FieldSize, PrimeCurve, }, - core::{ - convert::{TryFrom, TryInto}, - ops::Add, - }, - generic_array::{typenum::U1, ArrayLength}, + core::convert::{TryFrom, TryInto}, }; #[cfg(all(docsrs, feature = "pkcs8"))] @@ -184,8 +180,7 @@ where pub fn from_sec1_der(der_bytes: &[u8]) -> Result where C: PrimeCurve + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { sec1::EcPrivateKey::try_from(der_bytes)? .try_into() @@ -202,8 +197,7 @@ where where C: PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { // TODO(tarcieri): wrap `secret_key_bytes` in `Zeroizing` let mut private_key_bytes = self.to_be_bytes(); @@ -236,8 +230,7 @@ where pub fn from_sec1_pem(s: &str) -> Result where C: PrimeCurve + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { let (label, der_bytes) = pem::decode_vec(s.as_bytes()).map_err(|_| Error)?; @@ -258,8 +251,7 @@ where where C: PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { self.to_sec1_der() .ok() @@ -274,8 +266,7 @@ where pub fn from_jwk(jwk: &JwkEcKey) -> Result where C: JwkParameters + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { Self::try_from(jwk) } @@ -286,8 +277,7 @@ where pub fn from_jwk_str(jwk: &str) -> Result where C: JwkParameters + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { jwk.parse::().and_then(|jwk| Self::from_jwk(&jwk)) } @@ -300,8 +290,7 @@ where where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { self.into() } @@ -314,8 +303,7 @@ where where C: PrimeCurve + JwkParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { Zeroizing::new(self.to_jwk().to_string()) } @@ -365,8 +353,7 @@ where impl TryFrom> for SecretKey where C: PrimeCurve + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { type Error = der::Error; diff --git a/elliptic-curve/src/secret_key/pkcs8.rs b/elliptic-curve/src/secret_key/pkcs8.rs index 9a8d09539..24ecfbb14 100644 --- a/elliptic-curve/src/secret_key/pkcs8.rs +++ b/elliptic-curve/src/secret_key/pkcs8.rs @@ -2,12 +2,11 @@ use super::SecretKey; use crate::{ - sec1::{UncompressedPointSize, UntaggedPointSize, ValidatePublicKey}, - AlgorithmParameters, PrimeCurve, ALGORITHM_OID, + sec1::{ModulusSize, ValidatePublicKey}, + AlgorithmParameters, FieldSize, PrimeCurve, ALGORITHM_OID, }; -use core::{convert::TryFrom, ops::Add}; +use core::convert::TryFrom; use der::Decodable; -use generic_array::{typenum::U1, ArrayLength}; use pkcs8::FromPrivateKey; use sec1::EcPrivateKey; @@ -33,8 +32,7 @@ use { impl FromPrivateKey for SecretKey where C: PrimeCurve + AlgorithmParameters + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn from_pkcs8_private_key_info( private_key_info: pkcs8::PrivateKeyInfo<'_>, @@ -58,8 +56,7 @@ impl ToPrivateKey for SecretKey where C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { fn to_pkcs8_der(&self) -> pkcs8::Result { let ec_private_key = self.to_sec1_der()?; @@ -72,8 +69,7 @@ where impl FromStr for SecretKey where C: PrimeCurve + AlgorithmParameters + ValidatePublicKey, - UntaggedPointSize: Add + ArrayLength, - UncompressedPointSize: ArrayLength, + FieldSize: ModulusSize, { type Err = Error;