Skip to content

ed448-goldilocks: reuse types from ed448 #1224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 5 additions & 5 deletions ed448-goldilocks/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ This crate also includes signing and verifying of Ed448 signatures.
[dependencies]
crypto_signature = { version = "3.0.0-rc.0", default-features = false, features = ["digest", "rand_core"], optional = true, package = "signature" }
elliptic-curve = { version = "0.14.0-rc.5", features = ["arithmetic", "hash2curve", "pkcs8"] }
ed448 = { version = "=0.5.0-pre.0", default-features = false, optional = true }
rand_core = { version = "0.9", default-features = false }
serdect = { version = "0.3.0", optional = true }
sha3 = { version = "0.11.0-rc.0", default-features = false }
subtle = { version = "2.6", default-features = false }

[features]
default = ["std", "signing", "pkcs8"]
alloc = ["crypto_signature/alloc", "elliptic-curve/alloc", "serdect/alloc"]
alloc = ["crypto_signature/alloc", "ed448?/alloc", "elliptic-curve/alloc", "serdect/alloc"]
std = ["alloc"]

bits = ["elliptic-curve/bits"]
pkcs8 = ["elliptic-curve/pkcs8"]
signing = ["dep:crypto_signature"]
serde = ["dep:serdect"]
pkcs8 = ["ed448?/pkcs8", "elliptic-curve/pkcs8"]
signing = ["dep:crypto_signature", "ed448"]
serde = ["dep:serdect", "ed448?/serde_bytes"]

[dev-dependencies]
hex-literal = "1"
Expand Down
26 changes: 7 additions & 19 deletions ed448-goldilocks/src/curve/edwards/extended.rs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ impl TryFrom<&[u8]> for CompressedEdwardsY {

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
let bytes = <PointBytes>::try_from(value).map_err(|_| "Invalid length")?;
Self::try_from(&bytes)
Ok(CompressedEdwardsY(bytes))
}
}

Expand All @@ -173,24 +173,6 @@ impl From<&CompressedEdwardsY> for PointBytes {
}
}

impl TryFrom<PointBytes> for CompressedEdwardsY {
type Error = &'static str;

fn try_from(value: PointBytes) -> Result<Self, Self::Error> {
let pt = CompressedEdwardsY(value);
let _ = Option::<EdwardsPoint>::from(pt.decompress()).ok_or("Invalid point")?;
Ok(pt)
}
}

impl TryFrom<&PointBytes> for CompressedEdwardsY {
type Error = &'static str;

fn try_from(value: &PointBytes) -> Result<Self, Self::Error> {
Self::try_from(*value)
}
}

#[cfg(feature = "serde")]
impl serdect::serde::Serialize for CompressedEdwardsY {
fn serialize<S: serdect::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
Expand All @@ -210,6 +192,12 @@ impl<'de> serdect::serde::Deserialize<'de> for CompressedEdwardsY {
}
}

impl From<PointBytes> for CompressedEdwardsY {
fn from(point: PointBytes) -> Self {
Self(point)
}
}

impl CompressedEdwardsY {
/// The compressed generator point
pub const GENERATOR: Self = Self([
Expand Down
173 changes: 13 additions & 160 deletions ed448-goldilocks/src/sign/signature.rs
Original file line number Diff line number Diff line change
@@ -1,160 +1,24 @@
use crate::*;
use elliptic_curve::Group;
use elliptic_curve::array::Array;

/// Ed448 signature as defined in [RFC8032 § 5.2.5]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub struct Signature {
pub(crate) r: CompressedEdwardsY,
pub(crate) s: [u8; 57],
}

impl Default for Signature {
fn default() -> Self {
Self {
r: CompressedEdwardsY::default(),
s: [0u8; 57],
}
}
}

#[cfg(feature = "alloc")]
impl TryFrom<Vec<u8>> for Signature {
type Error = SigningError;

fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(value.as_slice())
}
}

#[cfg(feature = "alloc")]
impl TryFrom<&Vec<u8>> for Signature {
type Error = SigningError;

fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
Self::try_from(value.as_slice())
}
}

impl TryFrom<&[u8]> for Signature {
type Error = SigningError;

fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
if value.len() != SIGNATURE_LENGTH {
return Err(SigningError::InvalidSignatureLength);
}

let mut bytes = [0u8; SIGNATURE_LENGTH];
bytes.copy_from_slice(value);
Self::from_bytes(&bytes)
}
}

#[cfg(feature = "alloc")]
impl TryFrom<Box<[u8]>> for Signature {
type Error = SigningError;

fn try_from(value: Box<[u8]>) -> Result<Self, Self::Error> {
Self::try_from(value.as_ref())
}
}

#[cfg(feature = "serde")]
impl serdect::serde::Serialize for Signature {
fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
where
S: serdect::serde::Serializer,
{
serdect::array::serialize_hex_lower_or_bin(&self.to_bytes(), s)
}
}

#[cfg(feature = "serde")]
impl<'de> serdect::serde::Deserialize<'de> for Signature {
fn deserialize<D>(d: D) -> Result<Self, D::Error>
where
D: serdect::serde::Deserializer<'de>,
{
let mut bytes = [0u8; SIGNATURE_LENGTH];
serdect::array::deserialize_hex_or_bin(&mut bytes, d)?;
Signature::from_bytes(&bytes).map_err(serdect::serde::de::Error::custom)
}
}

impl Signature {
/// Converts [`Signature`] to a byte array.
pub fn to_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
let mut bytes = [0u8; SIGNATURE_LENGTH];
bytes[..57].copy_from_slice(self.r.as_bytes());
bytes[57..].copy_from_slice(&self.s);
bytes
}

/// Converts a byte array to a [`Signature`].
pub fn from_bytes(bytes: &[u8; SIGNATURE_LENGTH]) -> Result<Self, SigningError> {
let mut r = [0u8; SECRET_KEY_LENGTH];
r.copy_from_slice(&bytes[..SECRET_KEY_LENGTH]);
let mut s = [0u8; SECRET_KEY_LENGTH];
s.copy_from_slice(&bytes[SECRET_KEY_LENGTH..]);

let r = CompressedEdwardsY(r);

let big_r = r.decompress();
if big_r.is_none().into() {
return Err(SigningError::InvalidSignatureRComponent);
}

let big_r = big_r.expect("big_r is not none");
if big_r.is_identity().into() {
return Err(SigningError::InvalidSignatureRComponent);
}

if s[56] != 0x00 {
return Err(SigningError::InvalidSignatureSComponent);
}
let s_bytes = ScalarBytes::from(s);
let ss = Scalar::from_canonical_bytes(&s_bytes);

if ss.is_none().into() {
return Err(SigningError::InvalidSignatureSComponent);
}
let sc = ss.expect("ss is not none");
if sc.is_zero().into() {
return Err(SigningError::InvalidSignatureSComponent);
}

Ok(Self { r, s })
}

/// The `r` value of the signature.
pub fn r(&self) -> CompressedEdwardsY {
self.r
}

/// The `s` value of the signature.
pub fn s(&self) -> &[u8; SECRET_KEY_LENGTH] {
&self.s
}
}
pub use ed448::Signature;

impl From<InnerSignature> for Signature {
fn from(inner: InnerSignature) -> Self {
let mut s = [0u8; SECRET_KEY_LENGTH];
s.copy_from_slice(&inner.s.to_bytes_rfc_8032());
Self {
r: inner.r.compress(),
s,
}
Self::from_components(inner.r.compress(), s)
}
}

impl TryFrom<Signature> for InnerSignature {
impl TryFrom<&Signature> for InnerSignature {
type Error = SigningError;

fn try_from(signature: Signature) -> Result<Self, Self::Error> {
let s_bytes = ScalarBytes::try_from(&signature.s[..]).expect("invalid length");
let s = Option::from(Scalar::from_canonical_bytes(&s_bytes))
fn try_from(signature: &Signature) -> Result<Self, Self::Error> {
let s_bytes: &Array<u8, _> = (signature.s_bytes()).into();
let s = Option::from(Scalar::from_canonical_bytes(s_bytes))
.ok_or(SigningError::InvalidSignatureSComponent)?;
let r = Option::from(signature.r.decompress())
let r = Option::from(CompressedEdwardsY::from(*signature.r_bytes()).decompress())
.ok_or(SigningError::InvalidSignatureRComponent)?;
Ok(Self { r, s })
}
Expand All @@ -165,21 +29,10 @@ pub(crate) struct InnerSignature {
pub(crate) s: Scalar,
}

#[cfg(feature = "serde")]
#[test]
fn serialization() {
use rand_chacha::ChaCha8Rng;
use rand_core::SeedableRng;

let mut rng = ChaCha8Rng::from_seed([0u8; 32]);
let signing_key = super::SigningKey::generate(&mut rng);
let signature = signing_key.sign_raw(b"Hello, World!");

let bytes = serde_bare::to_vec(&signature).unwrap();
let signature2: Signature = serde_bare::from_slice(&bytes).unwrap();
assert_eq!(signature, signature2);
impl TryFrom<Signature> for InnerSignature {
type Error = SigningError;

let string = serde_json::to_string(&signature).unwrap();
let signature3: Signature = serde_json::from_str(&string).unwrap();
assert_eq!(signature, signature3);
fn try_from(signature: Signature) -> Result<Self, Self::Error> {
Self::try_from(&signature)
}
}
Loading