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