Skip to main content

aws_lc_rs/pqdsa/
signature.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0 OR ISC
3
4use crate::aws_lc::EVP_PKEY;
5use crate::buffer::Buffer;
6use crate::digest::Digest;
7use crate::encoding::{AsDer, PublicKeyX509Der};
8use crate::error::Unspecified;
9use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
10use crate::pqdsa::{parse_pqdsa_public_key, AlgorithmID};
11use crate::ptr::LcPtr;
12use crate::signature::{ParsedPublicKey, ParsedVerificationAlgorithm, VerificationAlgorithm};
13use crate::{digest, sealed};
14use core::fmt;
15use core::fmt::{Debug, Formatter};
16#[cfg(feature = "ring-sig-verify")]
17use untrusted::Input;
18
19/// A PQDSA verification algorithm.
20#[derive(Debug, Eq, PartialEq)]
21pub struct PqdsaVerificationAlgorithm {
22    pub(crate) id: &'static AlgorithmID,
23}
24
25impl sealed::Sealed for PqdsaVerificationAlgorithm {}
26
27/// A PQDSA signing algorithm.
28#[derive(Debug, Eq, PartialEq)]
29pub struct PqdsaSigningAlgorithm(pub(crate) &'static PqdsaVerificationAlgorithm);
30
31impl PqdsaSigningAlgorithm {
32    /// Returns the size of the signature in bytes.
33    #[must_use]
34    pub fn signature_len(&self) -> usize {
35        self.0.id.signature_size_bytes()
36    }
37
38    /// Returns the size of the raw public key in bytes.
39    #[must_use]
40    pub fn public_key_len(&self) -> usize {
41        self.0.id.pub_key_size_bytes()
42    }
43
44    /// Returns the size of the seed in bytes.
45    ///
46    /// See [`crate::signature::PqdsaKeyPair::from_seed`].
47    #[must_use]
48    pub fn seed_len(&self) -> usize {
49        self.0.id.seed_size_bytes()
50    }
51}
52
53/// A PQDSA public key.
54#[derive(Clone)]
55pub struct PublicKey {
56    evp_pkey: LcPtr<EVP_PKEY>,
57    pub(crate) octets: Box<[u8]>,
58}
59unsafe impl Send for PublicKey {}
60
61unsafe impl Sync for PublicKey {}
62
63impl PublicKey {
64    pub(crate) fn from_private_evp_pkey(evp_pkey: &LcPtr<EVP_PKEY>) -> Result<Self, Unspecified> {
65        let octets = evp_pkey.as_const().marshal_raw_public_key()?;
66        Ok(Self {
67            evp_pkey: evp_pkey.clone(),
68            octets: octets.into_boxed_slice(),
69        })
70    }
71}
72
73impl ParsedVerificationAlgorithm for PqdsaVerificationAlgorithm {
74    fn parsed_verify_sig(
75        &self,
76        public_key: &ParsedPublicKey,
77        msg: &[u8],
78        signature: &[u8],
79    ) -> Result<(), Unspecified> {
80        let evp_pkey = public_key.key();
81        evp_pkey.verify(msg, None, No_EVP_PKEY_CTX_consumer, signature)
82    }
83
84    fn parsed_verify_digest_sig(
85        &self,
86        _public_key: &ParsedPublicKey,
87        _digest: &Digest,
88        _signature: &[u8],
89    ) -> Result<(), Unspecified> {
90        // This API cannot be used with ML-DSA, because a `Digest` cannot represent the
91        // input that ML-DSA's digest-then-verify flow ("external mu") requires. That flow
92        // verifies against the 64-byte message representative
93        // mu = SHAKE256(SHAKE256(public_key) || 0x00 || len(ctx) || ctx || message),
94        // which is a key-dependent XOF output rather than a fixed-output hash of the
95        // message alone. Previously this path verified the digest bytes as if they were
96        // a pure ML-DSA message, which is not a construction defined by FIPS 204.
97        Err(Unspecified)
98    }
99}
100
101impl VerificationAlgorithm for PqdsaVerificationAlgorithm {
102    /// Verifies the signature of `msg` using the public key `public_key`.
103    ///
104    /// # Errors
105    /// `error::Unspecified` if the signature is invalid.
106    #[cfg(feature = "ring-sig-verify")]
107    fn verify(
108        &self,
109        public_key: Input<'_>,
110        msg: Input<'_>,
111        signature: Input<'_>,
112    ) -> Result<(), Unspecified> {
113        self.verify_sig(
114            public_key.as_slice_less_safe(),
115            msg.as_slice_less_safe(),
116            signature.as_slice_less_safe(),
117        )
118    }
119
120    /// Verifies the signature for `msg` using the `public_key`.
121    ///
122    /// # Errors
123    /// `error::Unspecified` if the signature is invalid.
124    //
125    // # FIPS
126    // Approved for all supported algorithms: ML-DSA-44, ML-DSA-65, ML-DSA-87.
127    fn verify_sig(
128        &self,
129        public_key: &[u8],
130        msg: &[u8],
131        signature: &[u8],
132    ) -> Result<(), Unspecified> {
133        let evp_pkey = parse_pqdsa_public_key(public_key, self.id)?;
134
135        evp_pkey.verify(msg, None, No_EVP_PKEY_CTX_consumer, signature)
136    }
137
138    /// DO NOT USE. This function is required by `VerificationAlgorithm` but cannot be used
139    /// with ML-DSA: a `Digest` cannot represent `mu`, the key-dependent "message
140    /// representative" that ML-DSA's digest-then-verify flow ("external mu") operates on.
141    /// See `parsed_verify_digest_sig` for details.
142    ///
143    /// # Errors
144    /// Always returns `Unspecified`.
145    fn verify_digest_sig(
146        &self,
147        _public_key: &[u8],
148        _digest: &digest::Digest,
149        _signature: &[u8],
150    ) -> Result<(), Unspecified> {
151        Err(Unspecified)
152    }
153}
154
155impl AsRef<[u8]> for PublicKey {
156    /// Serializes the public key as a raw byte string.
157    fn as_ref(&self) -> &[u8] {
158        self.octets.as_ref()
159    }
160}
161
162impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
163    /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
164    /// # Errors
165    /// Returns an error if the public key fails to marshal to X.509.
166    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, crate::error::Unspecified> {
167        let der = self.evp_pkey.as_const().marshal_rfc5280_public_key()?;
168        Ok(PublicKeyX509Der::from(Buffer::new(der)))
169    }
170}
171
172impl Debug for PublicKey {
173    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
174        f.write_str(&format!(
175            "PqdsaPublicKey(\"{}\")",
176            crate::hex::encode(self.octets.as_ref())
177        ))
178    }
179}