ed448_goldilocks_plus/sign/
verifying_key.rs

1//! Much of this code is borrowed from Thomas Pornin's [CRRL Project](https://github.com/pornin/crrl/blob/main/src/ed448.rs)
2//! and adapted to mirror `ed25519-dalek`'s API.
3
4use crate::curve::edwards::extended::PointBytes;
5use crate::sign::HASH_HEAD;
6use crate::{
7    CompressedEdwardsY, Context, EdwardsPoint, PreHash, Scalar, ScalarBytes, Signature,
8    SigningError, WideScalarBytes, PUBLIC_KEY_LENGTH,
9};
10use core::{
11    fmt::{self, Debug, Formatter},
12    hash::{Hash, Hasher},
13};
14use crypto_signature::Error;
15use elliptic_curve::Group;
16use sha3::{
17    digest::{ExtendableOutput, Update, XofReader},
18    Digest, Shake256,
19};
20
21/// Ed448 public key as defined in [RFC8032 ยง 5.2.5]
22#[derive(Copy, Clone, Default, Eq)]
23pub struct VerifyingKey {
24    pub(crate) compressed: CompressedEdwardsY,
25    pub(crate) point: EdwardsPoint,
26}
27
28impl Debug for VerifyingKey {
29    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
30        write!(f, "VerifyingKey({:?}), {:?}", self.compressed, self.point)
31    }
32}
33
34impl AsRef<[u8]> for VerifyingKey {
35    fn as_ref(&self) -> &[u8] {
36        self.compressed.as_bytes()
37    }
38}
39
40impl Hash for VerifyingKey {
41    fn hash<H: Hasher>(&self, state: &mut H) {
42        self.compressed.as_bytes().hash(state);
43    }
44}
45
46impl PartialEq for VerifyingKey {
47    fn eq(&self, other: &Self) -> bool {
48        self.compressed.as_bytes() == other.compressed.as_bytes()
49    }
50}
51
52impl crypto_signature::Verifier<Signature> for VerifyingKey {
53    fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
54        self.verify_raw(signature, msg)
55    }
56}
57
58impl<D> crypto_signature::DigestVerifier<D, Signature> for VerifyingKey
59where
60    D: Digest,
61{
62    fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> {
63        let mut prehashed_message = [0u8; 64];
64        prehashed_message.copy_from_slice(digest.finalize().as_slice());
65        self.verify_inner(signature, 1, &[], &prehashed_message)
66    }
67}
68
69impl crypto_signature::Verifier<Signature> for Context<'_, '_, VerifyingKey> {
70    fn verify(&self, msg: &[u8], signature: &Signature) -> Result<(), Error> {
71        self.key.verify_ctx(signature, self.value, msg)
72    }
73}
74
75impl<D> crypto_signature::DigestVerifier<D, Signature> for Context<'_, '_, VerifyingKey>
76where
77    D: Digest,
78{
79    fn verify_digest(&self, digest: D, signature: &Signature) -> Result<(), Error> {
80        let mut prehashed_message = [0u8; 64];
81        prehashed_message.copy_from_slice(digest.finalize().as_slice());
82        self.key
83            .verify_inner(signature, 1, self.value, &prehashed_message)
84    }
85}
86
87#[cfg(feature = "pkcs8")]
88/// This type is primarily useful for decoding/encoding SPKI public key files (either DER or PEM)
89#[derive(Debug, Clone, Copy, Eq, PartialEq)]
90pub struct PublicKeyBytes(pub [u8; PUBLIC_KEY_LENGTH]);
91
92#[cfg(feature = "pkcs8")]
93impl TryFrom<PublicKeyBytes> for VerifyingKey {
94    type Error = pkcs8::spki::Error;
95
96    fn try_from(value: PublicKeyBytes) -> Result<Self, Self::Error> {
97        VerifyingKey::try_from(&value)
98    }
99}
100
101#[cfg(feature = "pkcs8")]
102impl TryFrom<&PublicKeyBytes> for VerifyingKey {
103    type Error = pkcs8::spki::Error;
104
105    fn try_from(value: &PublicKeyBytes) -> Result<Self, Self::Error> {
106        VerifyingKey::from_bytes(&value.0).map_err(|_| pkcs8::spki::Error::KeyMalformed)
107    }
108}
109
110#[cfg(feature = "pkcs8")]
111impl From<VerifyingKey> for PublicKeyBytes {
112    fn from(key: VerifyingKey) -> Self {
113        Self(key.compressed.to_bytes())
114    }
115}
116
117#[cfg(feature = "pkcs8")]
118impl pkcs8::EncodePublicKey for PublicKeyBytes {
119    fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
120        pkcs8::SubjectPublicKeyInfoRef {
121            algorithm: super::ALGORITHM_ID,
122            subject_public_key: pkcs8::der::asn1::BitStringRef::new(0, &self.0)?,
123        }
124        .try_into()
125    }
126}
127
128#[cfg(feature = "pkcs8")]
129impl TryFrom<pkcs8::spki::SubjectPublicKeyInfoRef<'_>> for PublicKeyBytes {
130    type Error = pkcs8::spki::Error;
131
132    fn try_from(value: pkcs8::spki::SubjectPublicKeyInfoRef<'_>) -> Result<Self, Self::Error> {
133        value.algorithm.assert_algorithm_oid(super::ALGORITHM_OID)?;
134
135        if value.algorithm.parameters.is_some() {
136            return Err(pkcs8::spki::Error::KeyMalformed);
137        }
138
139        value
140            .subject_public_key
141            .as_bytes()
142            .ok_or(pkcs8::spki::Error::KeyMalformed)?
143            .try_into()
144            .map(Self)
145            .map_err(|_| pkcs8::spki::Error::KeyMalformed)
146    }
147}
148
149#[cfg(all(any(feature = "alloc", feature = "std"), feature = "pkcs8"))]
150impl pkcs8::EncodePublicKey for VerifyingKey {
151    fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
152        PublicKeyBytes::from(*self).to_public_key_der()
153    }
154}
155
156#[cfg(all(feature = "alloc", feature = "pkcs8"))]
157impl pkcs8::spki::DynSignatureAlgorithmIdentifier for VerifyingKey {
158    fn signature_algorithm_identifier(
159        &self,
160    ) -> pkcs8::spki::Result<pkcs8::spki::AlgorithmIdentifierOwned> {
161        // From https://datatracker.ietf.org/doc/html/rfc8410
162        Ok(pkcs8::spki::AlgorithmIdentifierOwned {
163            oid: super::ALGORITHM_OID,
164            parameters: None,
165        })
166    }
167}
168
169#[cfg(feature = "pkcs8")]
170impl TryFrom<pkcs8::spki::SubjectPublicKeyInfoRef<'_>> for VerifyingKey {
171    type Error = pkcs8::spki::Error;
172
173    fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
174        PublicKeyBytes::try_from(public_key)?.try_into()
175    }
176}
177
178#[cfg(feature = "serde")]
179impl serdect::serde::Serialize for VerifyingKey {
180    fn serialize<S: serdect::serde::Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
181        serdect::array::serialize_hex_lower_or_bin(self.compressed.as_bytes(), s)
182    }
183}
184
185#[cfg(feature = "serde")]
186impl<'de> serdect::serde::Deserialize<'de> for VerifyingKey {
187    fn deserialize<D: serdect::serde::Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
188        let mut bytes = [0u8; PUBLIC_KEY_LENGTH];
189        serdect::array::deserialize_hex_or_bin(&mut bytes, d)?;
190        VerifyingKey::from_bytes(&bytes).map_err(serdect::serde::de::Error::custom)
191    }
192}
193
194impl VerifyingKey {
195    /// Convert this verifying key into byte slice
196    pub fn to_bytes(&self) -> PointBytes {
197        self.compressed.to_bytes()
198    }
199
200    /// View this public key as a byte slice.
201    pub fn as_bytes(&self) -> &PointBytes {
202        self.compressed.as_bytes()
203    }
204
205    /// Construct a `VerifyingKey` from a slice of bytes.
206    pub fn from_bytes(bytes: &PointBytes) -> Result<Self, Error> {
207        let compressed = CompressedEdwardsY(*bytes);
208        let point = Option::<EdwardsPoint>::from(compressed.decompress())
209            .ok_or(SigningError::InvalidPublicKeyBytes)?;
210        if point.is_identity().into() {
211            return Err(SigningError::InvalidPublicKeyBytes.into());
212        }
213        Ok(Self { compressed, point })
214    }
215
216    /// Create a context for this verifying key that can be used with [`DigestVerifier`].
217    pub fn with_context<'k, 'v>(&'k self, context: &'v [u8]) -> Context<'k, 'v, Self> {
218        Context {
219            key: self,
220            value: context,
221        }
222    }
223
224    /// Return the verifying key in Edwards form.
225    pub fn to_edwards(self) -> EdwardsPoint {
226        self.point
227    }
228
229    /// Verifies a signature on a message.
230    ///
231    /// This is the "Ed448" mode of RFC 8032 (no pre-hashing, a
232    /// context is provided). This is equivalent to `verify_ctx()`
233    /// with an empty (zero-length) context.
234    ///
235    /// Note: this function is not constant-time; it assumes that the
236    /// public key and signature value are public data.
237    pub fn verify_raw(&self, signature: &Signature, message: &[u8]) -> Result<(), Error> {
238        self.verify_inner(signature, 0, &[], message)
239    }
240
241    /// Verifies a signature on a message (with context).
242    ///
243    /// This is the "Ed448" mode of RFC 8032 (no pre-hashing, a
244    /// context is provided). The context string MUST have length at most
245    /// 255 bytes. Return value is `Ok` on a valid signature, `Error`
246    /// otherwise.
247    ///
248    /// Note: this function is not constant-time; it assumes that the
249    /// public key and signature value are public data.
250    pub fn verify_ctx(self, sig: &Signature, ctx: &[u8], message: &[u8]) -> Result<(), Error> {
251        self.verify_inner(sig, 0, ctx, message)
252    }
253
254    /// Verifies a signature on a hashed message.
255    ///
256    /// This is the "Ed448ph" mode of RFC 8032 (message is pre-hashed),
257    /// also known as "HashEdDSA on Curve448". The hashed message `prehashed_message`
258    /// is provided (presumably, that hash value was obtained with
259    /// SHAKE256 and a 64-byte output; the caller does the hashing itself).
260    /// A context string `ctx` is
261    /// also provided; it MUST have length at most 255 bytes. Return
262    /// value is `Ok` on a valid signature, `Error` otherwise.
263    ///
264    /// Note: this function is not constant-time; it assumes that the
265    /// public key and signature value are public data.
266    pub fn verify_prehashed<D>(
267        self,
268        sig: &Signature,
269        ctx: Option<&[u8]>,
270        mut prehashed_message: D,
271    ) -> Result<(), Error>
272    where
273        D: PreHash,
274    {
275        let mut m = [0u8; 64];
276        prehashed_message.fill_bytes(&mut m);
277        self.verify_inner(sig, 1, ctx.unwrap_or_default(), &m)
278    }
279
280    fn verify_inner(
281        &self,
282        signature: &Signature,
283        phflag: u8,
284        ctx: &[u8],
285        m: &[u8],
286    ) -> Result<(), Error> {
287        // `signature` should already be valid but check to make sure
288        // Note that the scalar itself uses only 56 bytes; the extra
289        // 57th byte must be 0x00.
290        if signature.s[56] != 0x00 {
291            return Err(SigningError::InvalidSignatureSComponent.into());
292        }
293        if self.point.is_identity().into() {
294            return Err(SigningError::InvalidPublicKeyBytes.into());
295        }
296
297        let r = Option::<EdwardsPoint>::from(signature.r.decompress())
298            .ok_or(SigningError::InvalidSignatureRComponent)?;
299        if r.is_identity().into() {
300            return Err(SigningError::InvalidSignatureRComponent.into());
301        }
302
303        let s_bytes = ScalarBytes::from_slice(&signature.s);
304        let s = Option::<Scalar>::from(Scalar::from_canonical_bytes(s_bytes))
305            .ok_or(SigningError::InvalidSignatureSComponent)?;
306
307        if s.is_zero().into() {
308            return Err(SigningError::InvalidSignatureSComponent.into());
309        }
310
311        // SHAKE256(dom4(F, C) || R || A || PH(M), 114) -> scalar k
312        let mut bytes = WideScalarBytes::default();
313        let clen = ctx.len() as u8;
314        let mut reader = Shake256::default()
315            .chain(HASH_HEAD)
316            .chain([phflag])
317            .chain([clen])
318            .chain(ctx)
319            .chain(signature.r.as_bytes())
320            .chain(self.compressed.as_bytes())
321            .chain(m)
322            .finalize_xof();
323        reader.read(&mut bytes);
324        let k = Scalar::from_bytes_mod_order_wide(&bytes);
325        // Check the verification equation [S]B = R + [k]A.
326        let lhs = EdwardsPoint::GENERATOR * s;
327        let rhs = r + (self.point * k);
328        if lhs == rhs {
329            Ok(())
330        } else {
331            Err(SigningError::Verify.into())
332        }
333    }
334}
335
336#[cfg(test)]
337mod tests {
338    use super::*;
339    use crate::{PreHasherXof, SecretKey, SigningKey, PUBLIC_KEY_LENGTH};
340
341    struct Ed448TestVector<'a> {
342        s: &'a str,
343        q: &'a str,
344        m: &'a str,
345        ph: bool,
346        ctx: &'a str,
347        sig: &'a str,
348    }
349
350    // Test vectors from RFC 8032.
351    const TEST_VECTORS: [Ed448TestVector; 6] = [
352        // Empty message, empty context.
353        Ed448TestVector {
354            s:   "6c82a562cb808d10d632be89c8513ebf6c929f34ddfa8c9f63c9960ef6e348a3528c8a3fcc2f044e39a3fc5b94492f8f032e7549a20098f95b",
355            q:   "5fd7449b59b461fd2ce787ec616ad46a1da1342485a70e1f8a0ea75d80e96778edf124769b46c7061bd6783df1e50f6cd1fa1abeafe8256180",
356            m:   "",
357            ph:  false,
358            ctx: "",
359            sig: "533a37f6bbe457251f023c0d88f976ae2dfb504a843e34d2074fd823d41a591f2b233f034f628281f2fd7a22ddd47d7828c59bd0a21bfd3980ff0d2028d4b18a9df63e006c5d1c2d345b925d8dc00b4104852db99ac5c7cdda8530a113a0f4dbb61149f05a7363268c71d95808ff2e652600",
360        },
361        // 1-byte message, empty context.
362        Ed448TestVector {
363            s:   "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
364            q:   "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
365            m:   "03",
366            ph:  false,
367            ctx: "",
368            sig: "26b8f91727bd62897af15e41eb43c377efb9c610d48f2335cb0bd0087810f4352541b143c4b981b7e18f62de8ccdf633fc1bf037ab7cd779805e0dbcc0aae1cbcee1afb2e027df36bc04dcecbf154336c19f0af7e0a6472905e799f1953d2a0ff3348ab21aa4adafd1d234441cf807c03a00",
369        },
370        // 1-byte message, 3-byte context.
371        Ed448TestVector {
372            s:   "c4eab05d357007c632f3dbb48489924d552b08fe0c353a0d4a1f00acda2c463afbea67c5e8d2877c5e3bc397a659949ef8021e954e0a12274e",
373            q:   "43ba28f430cdff456ae531545f7ecd0ac834a55d9358c0372bfa0c6c6798c0866aea01eb00742802b8438ea4cb82169c235160627b4c3a9480",
374            m:   "03",
375            ph:  false,
376            ctx: "666f6f",
377            sig: "d4f8f6131770dd46f40867d6fd5d5055de43541f8c5e35abbcd001b32a89f7d2151f7647f11d8ca2ae279fb842d607217fce6e042f6815ea000c85741de5c8da1144a6a1aba7f96de42505d7a7298524fda538fccbbb754f578c1cad10d54d0d5428407e85dcbc98a49155c13764e66c3c00",
378        },
379        // 256-byte message, empty context.
380        Ed448TestVector {
381            s:   "2ec5fe3c17045abdb136a5e6a913e32ab75ae68b53d2fc149b77e504132d37569b7e766ba74a19bd6162343a21c8590aa9cebca9014c636df5",
382            q:   "79756f014dcfe2079f5dd9e718be4171e2ef2486a08f25186f6bff43a9936b9bfe12402b08ae65798a3d81e22e9ec80e7690862ef3d4ed3a00",
383            m:   "15777532b0bdd0d1389f636c5f6b9ba734c90af572877e2d272dd078aa1e567cfa80e12928bb542330e8409f3174504107ecd5efac61ae7504dabe2a602ede89e5cca6257a7c77e27a702b3ae39fc769fc54f2395ae6a1178cab4738e543072fc1c177fe71e92e25bf03e4ecb72f47b64d0465aaea4c7fad372536c8ba516a6039c3c2a39f0e4d832be432dfa9a706a6e5c7e19f397964ca4258002f7c0541b590316dbc5622b6b2a6fe7a4abffd96105eca76ea7b98816af0748c10df048ce012d901015a51f189f3888145c03650aa23ce894c3bd889e030d565071c59f409a9981b51878fd6fc110624dcbcde0bf7a69ccce38fabdf86f3bef6044819de11",
384            ph:  false,
385            ctx: "",
386            sig: "c650ddbb0601c19ca11439e1640dd931f43c518ea5bea70d3dcde5f4191fe53f00cf966546b72bcc7d58be2b9badef28743954e3a44a23f880e8d4f1cfce2d7a61452d26da05896f0a50da66a239a8a188b6d825b3305ad77b73fbac0836ecc60987fd08527c1a8e80d5823e65cafe2a3d00",
387        },
388        // 3-byte message, pre-hashed, empty context.
389        Ed448TestVector {
390            s:   "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49",
391            q:   "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880",
392            m:   "616263",
393            ph:  true,
394            ctx: "",
395            sig: "822f6901f7480f3d5f562c592994d9693602875614483256505600bbc281ae381f54d6bce2ea911574932f52a4e6cadd78769375ec3ffd1b801a0d9b3f4030cd433964b6457ea39476511214f97469b57dd32dbc560a9a94d00bff07620464a3ad203df7dc7ce360c3cd3696d9d9fab90f00",
396        },
397        Ed448TestVector {
398            s:   "833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42ef7822e0d5104127dc05d6dbefde69e3ab2cec7c867c6e2c49",
399            q:   "259b71c19f83ef77a7abd26524cbdb3161b590a48f7d17de3ee0ba9c52beb743c09428a131d6b1b57303d90d8132c276d5ed3d5d01c0f53880",
400            m:   "616263",
401            ph:  true,
402            ctx: "666f6f",
403            sig: "c32299d46ec8ff02b54540982814dce9a05812f81962b649d528095916a2aa481065b1580423ef927ecf0af5888f90da0f6a9a85ad5dc3f280d91224ba9911a3653d00e484e2ce232521481c8658df304bb7745a73514cdb9bf3e15784ab71284f8d0704a608c54a6b62d97beb511d132100",
404        },
405    ];
406
407    #[test]
408    fn signatures() {
409        for tv in TEST_VECTORS.iter() {
410            let mut seed = SecretKey::default();
411            hex::decode_to_slice(tv.s, &mut seed).unwrap();
412            let mut q_enc = [0u8; PUBLIC_KEY_LENGTH];
413            hex::decode_to_slice(tv.q, &mut q_enc).unwrap();
414            let msg = hex::decode(tv.m).unwrap();
415            let ctx = hex::decode(tv.ctx).unwrap();
416            let mut sig = [0u8; 114];
417            hex::decode_to_slice(tv.sig, &mut sig[..]).unwrap();
418            let sig = Signature::try_from(&sig[..]).unwrap();
419
420            let skey = SigningKey::from(&seed);
421            assert_eq!(&q_enc[..], skey.verifying_key().as_bytes());
422            if tv.ph {
423                assert_eq!(
424                    skey.sign_prehashed::<PreHasherXof<Shake256>>(
425                        Some(&ctx[..]),
426                        Shake256::default().chain(&msg).into(),
427                    )
428                    .unwrap(),
429                    sig
430                );
431            } else {
432                assert_eq!(skey.sign_ctx(&ctx[..], &msg[..]).unwrap(), sig);
433                if ctx.len() == 0 {
434                    assert_eq!(skey.sign_raw(&msg[..]), sig);
435                }
436            }
437
438            let pkey = VerifyingKey::from_bytes(&q_enc).unwrap();
439            if tv.ph {
440                let mut reader = Shake256::default().chain(&msg).finalize_xof();
441                let mut hm = [0u8; 64];
442                reader.read(&mut hm);
443                assert!(pkey.verify_inner(&sig, 1, &ctx[..], &hm).is_ok());
444                assert!(pkey.verify_inner(&sig, 1, &[1u8], &hm).is_err());
445                hm[42] ^= 0x08;
446                assert!(pkey.verify_inner(&sig, 1, &ctx[..], &hm).is_err());
447            } else {
448                assert!(pkey.verify_ctx(&sig, &ctx[..], &msg[..]).is_ok());
449                assert!(pkey.verify_ctx(&sig, &[1u8], &msg[..]).is_err());
450                assert!(pkey.verify_ctx(&sig, &ctx[..], &[0u8]).is_err());
451                if ctx.len() == 0 {
452                    assert!(pkey.verify_raw(&sig, &msg[..]).is_ok());
453                }
454            }
455        }
456    }
457
458    #[cfg(feature = "serde")]
459    #[test]
460    fn serialization() {
461        use rand_chacha::ChaCha8Rng;
462        use rand_core::SeedableRng;
463
464        let mut rng = ChaCha8Rng::from_seed([0u8; 32]);
465        let signing_key = SigningKey::generate(&mut rng);
466        let verifying_key = signing_key.verifying_key();
467
468        let bytes = serde_bare::to_vec(&verifying_key).unwrap();
469        let verifying_key2: VerifyingKey = serde_bare::from_slice(&bytes).unwrap();
470        assert_eq!(verifying_key, verifying_key2);
471
472        let string = serde_json::to_string(&verifying_key).unwrap();
473        let verifying_key3: VerifyingKey = serde_json::from_str(&string).unwrap();
474        assert_eq!(verifying_key, verifying_key3);
475    }
476}