ed448_goldilocks/sign.rs
1//! Ed448 digital signatures implementation
2//!
3//! # Example
4//! Creating an ed448 signature.
5//!
6//! Generate a [`SigningKey`], which includes both the public and secret halves, using
7//! a cryptographically secure pseudorandom number generator (CSPRNG). Next sign a message
8//! to produce a [`Signature`]. Then verify the signature using the corresponding
9//! [`VerifyingKey`].
10//!
11#![cfg_attr(feature = "getrandom", doc = "```")]
12#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
13//! use ed448_goldilocks::{SigningKey, elliptic_curve::Generate};
14//!
15//! let signing_key = SigningKey::generate();
16//! let signature = signing_key.sign_raw(b"Hello, world!");
17//! let verifying_key = signing_key.verifying_key();
18//!
19//! assert!(verifying_key.verify_raw(&signature, b"Hello, world!").is_ok());
20//! ```
21//!
22//! This crate also supports using context specific strings when creating and verifying signatures.
23//! In addition, it supports the PKCS#8 standard for encoding and decoding keys, or raw byte forms
24//! using `to_bytes` and `from_bytes` methods. These store the [`SecretKey`] which is the prehash
25//! seed of the [`SigningKey`].
26//!
27//! # PKCS#8 Key Encoding
28//! PKCS#8 is a private key format with support for multiple algorithms. It can be encoded as
29//! binary (DER) or text (PEM). Use the `pkcs8` feature to enable this option.
30//!
31//! # Using Serde
32//! This crate supports serialization and deserialization using the `serde` if the preference
33//! is to encode the keys as other formats. Use the `serde` feature to enable this option.
34//!
35//! # Using Signature
36//! This crate supports signing using the traits defined in the `signature` crate like
37//! - [`Signer`]
38//! - [`DigestSigner`]
39//! - [`PrehashSigner`]
40//! - [`Verifier`]
41//! - [`DigestVerifier`]
42//!
43//! The crate is re-exported as `crypto-signature` for use in other crates.
44//!
45//! # Other Features
46//! Signing and verifying also supports custom digest and prehash algorithms.
47//! Any algorithm that implements [`PreHash`] and [`Digest`] can be used.
48//! However, there are two implementations provided in this crate:
49//!
50//! - [`PreHasherXmd`] which supports any implementation of a fixed length digest like SHA3-512.
51//! - [`PreHasherXof`] which supports any implementation of expandable output functions like SHAKE-256.
52//!
53//! # Example
54//! This is an example of using the SHAKE-256 algorithm to sign and verify a message
55//! which is the normal default anyway but performed explicitly.
56//!
57#![cfg_attr(feature = "getrandom", doc = "```")]
58#![cfg_attr(not(feature = "getrandom"), doc = "```ignore")]
59//! use ed448_goldilocks::{SigningKey, PreHasherXof, elliptic_curve::Generate};
60//! use shake::{Shake256, digest::Update};
61//!
62//! let msg = b"Hello World";
63//!
64//! let signing_key = SigningKey::generate();
65//! let signature = signing_key.sign_prehashed::<PreHasherXof<Shake256>>(
66//! None,
67//! Shake256::default().chain(msg).into(),
68//! ).unwrap();
69//!
70//! let verifying_key = signing_key.verifying_key();
71//! assert!(verifying_key.verify_prehashed::<PreHasherXof<Shake256>>(
72//! &signature,
73//! None,
74//! Shake256::default().chain(msg).into()
75//! ).is_ok());
76//! ```
77mod context;
78mod error;
79mod expanded;
80mod signing_key;
81mod verifying_key;
82
83pub use context::*;
84pub use ed448::Signature;
85#[cfg(feature = "pkcs8")]
86pub use elliptic_curve::pkcs8;
87pub use error::*;
88pub use signature;
89pub use signing_key::*;
90pub use verifying_key::*;
91
92use crate::{CompressedEdwardsY, EdwardsPoint, EdwardsScalar};
93use elliptic_curve::{array::Array, group::GroupEncoding};
94
95/// Length of a secret key in bytes
96pub const SECRET_KEY_LENGTH: usize = 57;
97
98/// Length of a public key in bytes
99pub const PUBLIC_KEY_LENGTH: usize = 57;
100
101/// Length of a signature in bytes
102pub const SIGNATURE_LENGTH: usize = 114;
103
104/// Constant string "SigEd448".
105pub(crate) const HASH_HEAD: [u8; 8] = [0x53, 0x69, 0x67, 0x45, 0x64, 0x34, 0x34, 0x38];
106
107#[cfg(feature = "pkcs8")]
108/// The OID for Ed448 as defined in [RFC8410 §2]
109pub const ALGORITHM_OID: pkcs8::ObjectIdentifier =
110 pkcs8::ObjectIdentifier::new_unwrap("1.3.101.113");
111
112#[cfg(feature = "pkcs8")]
113/// The `AlgorithmIdentifier` for Ed448 as defined in [RFC8410 §2]
114pub const ALGORITHM_ID: pkcs8::AlgorithmIdentifierRef<'static> = pkcs8::AlgorithmIdentifierRef {
115 oid: ALGORITHM_OID,
116 parameters: None,
117};
118
119impl From<InnerSignature> for Signature {
120 fn from(inner: InnerSignature) -> Self {
121 let mut s = [0u8; SECRET_KEY_LENGTH];
122 s.copy_from_slice(&inner.s.to_bytes_rfc_8032());
123 Self::from_components(inner.r.to_bytes(), s)
124 }
125}
126
127impl TryFrom<&Signature> for InnerSignature {
128 type Error = SigningError;
129
130 fn try_from(signature: &Signature) -> Result<Self, Self::Error> {
131 let s_bytes: &Array<u8, _> = (signature.s_bytes()).into();
132 let s = Option::from(EdwardsScalar::from_canonical_bytes(s_bytes))
133 .ok_or(SigningError::InvalidSignatureSComponent)?;
134 let r = CompressedEdwardsY::from(*signature.r_bytes())
135 .decompress()
136 .into_option()
137 .map(|point| point.to_edwards())
138 .ok_or(SigningError::InvalidSignatureRComponent)?;
139 Ok(Self { r, s })
140 }
141}
142
143pub(crate) struct InnerSignature {
144 pub(crate) r: EdwardsPoint,
145 pub(crate) s: EdwardsScalar,
146}
147
148impl TryFrom<Signature> for InnerSignature {
149 type Error = SigningError;
150
151 fn try_from(signature: Signature) -> Result<Self, Self::Error> {
152 Self::try_from(&signature)
153 }
154}