aws_lc_rs/ec/
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::{
5    ECDSA_SIG_new, ECDSA_SIG_set0, ECDSA_SIG_to_bytes, NID_X9_62_prime256v1, NID_secp256k1,
6    NID_secp384r1, NID_secp521r1, BIGNUM, ECDSA_SIG, EVP_PKEY,
7};
8
9use crate::digest::Digest;
10use crate::ec::compressed_public_key_size_bytes;
11use crate::ec::encoding::parse_ec_public_key;
12use crate::ec::encoding::sec1::marshal_sec1_public_point;
13use crate::encoding::{
14    AsBigEndian, AsDer, EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der,
15};
16use crate::error::Unspecified;
17use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
18use crate::ptr::{DetachableLcPtr, LcPtr};
19use crate::signature::{ParsedPublicKey, ParsedVerificationAlgorithm, VerificationAlgorithm};
20use crate::{digest, sealed};
21use core::fmt;
22use core::fmt::{Debug, Formatter};
23use std::mem::MaybeUninit;
24use std::ops::Deref;
25use std::ptr::null_mut;
26#[cfg(feature = "ring-sig-verify")]
27use untrusted::Input;
28
29/// An ECDSA verification algorithm.
30#[derive(Debug, Eq, PartialEq)]
31pub struct EcdsaVerificationAlgorithm {
32    pub(crate) id: &'static AlgorithmID,
33    pub(crate) digest: &'static digest::Algorithm,
34    pub(crate) sig_format: EcdsaSignatureFormat,
35}
36
37/// An ECDSA signing algorithm.
38#[derive(Debug, Eq, PartialEq)]
39pub struct EcdsaSigningAlgorithm(pub(crate) &'static EcdsaVerificationAlgorithm);
40
41impl Deref for EcdsaSigningAlgorithm {
42    type Target = EcdsaVerificationAlgorithm;
43    #[inline]
44    fn deref(&self) -> &Self::Target {
45        self.0
46    }
47}
48
49impl sealed::Sealed for EcdsaVerificationAlgorithm {}
50impl sealed::Sealed for EcdsaSigningAlgorithm {}
51
52#[derive(Debug, Eq, PartialEq)]
53pub(crate) enum EcdsaSignatureFormat {
54    ASN1,
55    Fixed,
56}
57
58#[derive(Debug, Eq, PartialEq)]
59#[allow(non_camel_case_types)]
60pub(crate) enum AlgorithmID {
61    ECDSA_P256,
62    ECDSA_P384,
63    ECDSA_P521,
64    ECDSA_P256K1,
65}
66
67impl AlgorithmID {
68    #[inline]
69    pub(crate) fn nid(&'static self) -> i32 {
70        match self {
71            AlgorithmID::ECDSA_P256 => NID_X9_62_prime256v1,
72            AlgorithmID::ECDSA_P384 => NID_secp384r1,
73            AlgorithmID::ECDSA_P521 => NID_secp521r1,
74            AlgorithmID::ECDSA_P256K1 => NID_secp256k1,
75        }
76    }
77    pub(crate) fn private_key_size(&self) -> usize {
78        match self {
79            AlgorithmID::ECDSA_P256 | AlgorithmID::ECDSA_P256K1 => 32,
80            AlgorithmID::ECDSA_P384 => 48,
81            AlgorithmID::ECDSA_P521 => 66,
82        }
83    }
84    // Compressed public key length in bytes
85    #[inline]
86    #[allow(dead_code)]
87    const fn compressed_pub_key_len(&self) -> usize {
88        match self {
89            AlgorithmID::ECDSA_P256 | AlgorithmID::ECDSA_P256K1 => {
90                compressed_public_key_size_bytes(256)
91            }
92            AlgorithmID::ECDSA_P384 => compressed_public_key_size_bytes(384),
93            AlgorithmID::ECDSA_P521 => compressed_public_key_size_bytes(521),
94        }
95    }
96}
97
98/// Elliptic curve public key.
99#[derive(Clone)]
100pub struct PublicKey {
101    #[allow(dead_code)]
102    algorithm: &'static EcdsaSigningAlgorithm,
103    evp_pkey: LcPtr<EVP_PKEY>,
104    octets: Box<[u8]>,
105}
106
107pub(crate) fn public_key_from_evp_pkey(
108    evp_pkey: &LcPtr<EVP_PKEY>,
109    algorithm: &'static EcdsaSigningAlgorithm,
110) -> Result<PublicKey, Unspecified> {
111    let pub_key_bytes = marshal_sec1_public_point(evp_pkey, false)?;
112
113    Ok(PublicKey {
114        evp_pkey: evp_pkey.clone(),
115        algorithm,
116        octets: pub_key_bytes.into_boxed_slice(),
117    })
118}
119
120impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
121    /// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
122    /// # Errors
123    /// Returns an error if the public key fails to marshal to X.509.
124    fn as_der(&self) -> Result<PublicKeyX509Der<'static>, Unspecified> {
125        let der = self.evp_pkey.as_const().marshal_rfc5280_public_key()?;
126        Ok(PublicKeyX509Der::new(der))
127    }
128}
129
130impl AsBigEndian<EcPublicKeyCompressedBin<'static>> for PublicKey {
131    /// Provides the public key elliptic curve point to a compressed point bytes format.
132    /// # Errors
133    /// Returns an error if the public key fails to marshal.
134    fn as_be_bytes(&self) -> Result<EcPublicKeyCompressedBin<'static>, crate::error::Unspecified> {
135        let pub_point = marshal_sec1_public_point(&self.evp_pkey, true)?;
136        Ok(EcPublicKeyCompressedBin::new(pub_point))
137    }
138}
139
140impl AsBigEndian<EcPublicKeyUncompressedBin<'static>> for PublicKey {
141    /// Provides the public key elliptic curve point to an uncompressed point bytes format.
142    /// # Errors
143    /// Returns an error if the public key fails to marshal.
144    fn as_be_bytes(
145        &self,
146    ) -> Result<EcPublicKeyUncompressedBin<'static>, crate::error::Unspecified> {
147        let mut uncompressed_bytes = vec![0u8; self.octets.len()];
148        uncompressed_bytes.copy_from_slice(&self.octets);
149        Ok(EcPublicKeyUncompressedBin::new(uncompressed_bytes))
150    }
151}
152
153impl Debug for PublicKey {
154    fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
155        f.write_str(&format!(
156            "EcdsaPublicKey(\"{}\")",
157            crate::hex::encode(self.octets.as_ref())
158        ))
159    }
160}
161
162impl AsRef<[u8]> for PublicKey {
163    #[inline]
164    /// Serializes the public key in an uncompressed form (X9.62) using the
165    /// Octet-String-to-Elliptic-Curve-Point algorithm in
166    /// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
167    fn as_ref(&self) -> &[u8] {
168        self.octets.as_ref()
169    }
170}
171
172unsafe impl Send for PublicKey {}
173unsafe impl Sync for PublicKey {}
174
175impl VerificationAlgorithm for EcdsaVerificationAlgorithm {
176    #[inline]
177    #[cfg(feature = "ring-sig-verify")]
178    fn verify(
179        &self,
180        public_key: Input<'_>,
181        msg: Input<'_>,
182        signature: Input<'_>,
183    ) -> Result<(), Unspecified> {
184        self.verify_sig(
185            public_key.as_slice_less_safe(),
186            msg.as_slice_less_safe(),
187            signature.as_slice_less_safe(),
188        )
189    }
190
191    fn verify_sig(
192        &self,
193        public_key: &[u8],
194        msg: &[u8],
195        signature: &[u8],
196    ) -> Result<(), Unspecified> {
197        let public_key = parse_ec_public_key(public_key, self.id.nid())?;
198        self.verify_ecdsa(msg, signature, &public_key)
199    }
200
201    fn verify_digest_sig(
202        &self,
203        public_key: &[u8],
204        digest: &digest::Digest,
205        signature: &[u8],
206    ) -> Result<(), Unspecified> {
207        let public_key = parse_ec_public_key(public_key, self.id.nid())?;
208
209        self.verify_digest_ecdsa(digest, signature, &public_key)
210    }
211}
212
213impl EcdsaVerificationAlgorithm {
214    fn verify_ecdsa(
215        &self,
216        msg: &[u8],
217        signature: &[u8],
218        public_key: &LcPtr<EVP_PKEY>,
219    ) -> Result<(), Unspecified> {
220        match self.sig_format {
221            EcdsaSignatureFormat::ASN1 => {
222                verify_asn1_signature(self.digest, public_key, msg, signature)
223            }
224            EcdsaSignatureFormat::Fixed => {
225                let (out_bytes, out_bytes_len) = convert_fixed_signature(self.id, signature)?;
226                verify_asn1_signature(self.digest, public_key, msg, unsafe {
227                    out_bytes.as_slice(out_bytes_len)
228                })
229            }
230        }
231    }
232
233    fn verify_digest_ecdsa(
234        &self,
235        digest: &Digest,
236        signature: &[u8],
237        public_key: &LcPtr<EVP_PKEY>,
238    ) -> Result<(), Unspecified> {
239        if self.digest != digest.algorithm() {
240            return Err(Unspecified);
241        }
242        match self.sig_format {
243            EcdsaSignatureFormat::ASN1 => {
244                verify_asn1_digest_signature(digest, public_key, signature)
245            }
246            EcdsaSignatureFormat::Fixed => {
247                let (out_bytes, out_bytes_len) = convert_fixed_signature(self.id, signature)?;
248                verify_asn1_digest_signature(digest, public_key, unsafe {
249                    out_bytes.as_slice(out_bytes_len)
250                })
251            }
252        }
253    }
254}
255
256impl ParsedVerificationAlgorithm for EcdsaVerificationAlgorithm {
257    fn parsed_verify_sig(
258        &self,
259        public_key: &ParsedPublicKey,
260        msg: &[u8],
261        signature: &[u8],
262    ) -> Result<(), Unspecified> {
263        self.verify_ecdsa(msg, signature, public_key.key())
264    }
265
266    fn parsed_verify_digest_sig(
267        &self,
268        public_key: &ParsedPublicKey,
269        digest: &Digest,
270        signature: &[u8],
271    ) -> Result<(), Unspecified> {
272        self.verify_digest_ecdsa(digest, signature, public_key.key())
273    }
274}
275
276fn convert_fixed_signature(
277    alg: &'static AlgorithmID,
278    signature: &[u8],
279) -> Result<(LcPtr<u8>, usize), Unspecified> {
280    let mut out_bytes = null_mut::<u8>();
281    let mut out_bytes_len = MaybeUninit::<usize>::uninit();
282    let sig = unsafe { ecdsa_sig_from_fixed(alg, signature)? };
283    if 1 != unsafe {
284        ECDSA_SIG_to_bytes(&mut out_bytes, out_bytes_len.as_mut_ptr(), *sig.as_const())
285    } {
286        return Err(Unspecified);
287    }
288    Ok((LcPtr::new(out_bytes)?, unsafe {
289        out_bytes_len.assume_init()
290    }))
291}
292
293fn verify_asn1_signature(
294    digest_alg: &'static digest::Algorithm,
295    public_key: &LcPtr<EVP_PKEY>,
296    msg: &[u8],
297    signature: &[u8],
298) -> Result<(), Unspecified> {
299    public_key.verify(msg, Some(digest_alg), No_EVP_PKEY_CTX_consumer, signature)
300}
301
302fn verify_asn1_digest_signature(
303    digest: &Digest,
304    public_key: &LcPtr<EVP_PKEY>,
305    signature: &[u8],
306) -> Result<(), Unspecified> {
307    public_key.verify_digest_sig(digest, No_EVP_PKEY_CTX_consumer, signature)
308}
309
310#[inline]
311unsafe fn ecdsa_sig_from_fixed(
312    alg_id: &'static AlgorithmID,
313    signature: &[u8],
314) -> Result<LcPtr<ECDSA_SIG>, ()> {
315    let num_size_bytes = alg_id.private_key_size();
316    if signature.len() != 2 * num_size_bytes {
317        return Err(());
318    }
319    let mut r_bn = DetachableLcPtr::<BIGNUM>::try_from(&signature[..num_size_bytes])?;
320    let mut s_bn = DetachableLcPtr::<BIGNUM>::try_from(&signature[num_size_bytes..])?;
321
322    let mut ecdsa_sig = LcPtr::new(ECDSA_SIG_new())?;
323
324    if 1 != ECDSA_SIG_set0(*ecdsa_sig.as_mut(), *r_bn.as_mut(), *s_bn.as_mut()) {
325        return Err(());
326    }
327    r_bn.detach();
328    s_bn.detach();
329
330    Ok(ecdsa_sig)
331}