Skip to content

Commit f013911

Browse files
authored
Refactor ECDSA structs into ec::signature (#654)
1 parent 2d48ab5 commit f013911

File tree

4 files changed

+354
-334
lines changed

4 files changed

+354
-334
lines changed

aws-lc-rs/src/ec.rs

+8-306
Original file line numberDiff line numberDiff line change
@@ -3,47 +3,36 @@
33
// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
44
// SPDX-License-Identifier: Apache-2.0 OR ISC
55

6-
use core::fmt;
7-
use core::fmt::{Debug, Formatter};
6+
use crate::ec::signature::AlgorithmID;
87
use core::mem::MaybeUninit;
9-
use core::ops::Deref;
108
use core::ptr::null;
119
use core::ptr::null_mut;
1210
// TODO: Uncomment when MSRV >= 1.64
1311
// use core::ffi::c_int;
1412
use std::os::raw::c_int;
1513

16-
#[cfg(feature = "ring-sig-verify")]
17-
use untrusted::Input;
18-
1914
#[cfg(feature = "fips")]
2015
use aws_lc::EC_KEY_check_fips;
2116
#[cfg(not(feature = "fips"))]
2217
use aws_lc::EC_KEY_check_key;
2318
use aws_lc::{
2419
d2i_PrivateKey, point_conversion_form_t, BN_bn2bin_padded, BN_num_bytes, CBS_init,
25-
ECDSA_SIG_from_bytes, ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, ECDSA_SIG_new, ECDSA_SIG_set0,
26-
ECDSA_SIG_to_bytes, EC_GROUP_get_curve_name, EC_GROUP_new_by_curve_name, EC_KEY_get0_group,
27-
EC_KEY_get0_private_key, EC_KEY_get0_public_key, EC_KEY_new, EC_KEY_set_group,
28-
EC_KEY_set_private_key, EC_KEY_set_public_key, EC_POINT_mul, EC_POINT_new, EC_POINT_oct2point,
29-
EC_POINT_point2oct, EVP_DigestVerify, EVP_DigestVerifyInit, EVP_PKEY_CTX_new_id,
20+
ECDSA_SIG_from_bytes, ECDSA_SIG_get0_r, ECDSA_SIG_get0_s, EC_GROUP_get_curve_name,
21+
EC_GROUP_new_by_curve_name, EC_KEY_get0_group, EC_KEY_get0_private_key, EC_KEY_get0_public_key,
22+
EC_KEY_new, EC_KEY_set_group, EC_KEY_set_private_key, EC_KEY_set_public_key, EC_POINT_mul,
23+
EC_POINT_new, EC_POINT_oct2point, EC_POINT_point2oct, EVP_PKEY_CTX_new_id,
3024
EVP_PKEY_CTX_set_ec_paramgen_curve_nid, EVP_PKEY_assign_EC_KEY, EVP_PKEY_get0_EC_KEY,
31-
EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, EVP_parse_public_key,
32-
NID_X9_62_prime256v1, NID_secp256k1, NID_secp384r1, NID_secp521r1, BIGNUM, CBS, ECDSA_SIG,
25+
EVP_PKEY_keygen, EVP_PKEY_keygen_init, EVP_PKEY_new, EVP_parse_public_key, BIGNUM, CBS,
3326
EC_GROUP, EC_KEY, EC_POINT, EVP_PKEY, EVP_PKEY_EC,
3427
};
3528

36-
use crate::digest::digest_ctx::DigestContext;
37-
use crate::encoding::{
38-
AsBigEndian, AsDer, EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, PublicKeyX509Der,
39-
};
4029
use crate::error::{KeyRejected, Unspecified};
4130
use crate::fips::indicator_check;
4231
use crate::ptr::{ConstPointer, DetachableLcPtr, LcPtr};
43-
use crate::signature::{Signature, VerificationAlgorithm};
44-
use crate::{digest, hex, sealed};
32+
use crate::signature::Signature;
4533

4634
pub(crate) mod key_pair;
35+
pub(crate) mod signature;
4736

4837
const ELEM_MAX_BITS: usize = 521;
4938
pub(crate) const ELEM_MAX_BYTES: usize = (ELEM_MAX_BITS + 7) / 8;
@@ -64,256 +53,6 @@ pub(crate) const PUBLIC_KEY_MAX_LEN: usize = 1 + (2 * ELEM_MAX_BYTES);
6453
/// `42` is the length of the P-521 template.
6554
pub const PKCS8_DOCUMENT_MAX_LEN: usize = 42 + SCALAR_MAX_BYTES + PUBLIC_KEY_MAX_LEN;
6655

67-
/// An ECDSA verification algorithm.
68-
#[derive(Debug, Eq, PartialEq)]
69-
pub struct EcdsaVerificationAlgorithm {
70-
pub(super) id: &'static AlgorithmID,
71-
pub(super) digest: &'static digest::Algorithm,
72-
pub(super) sig_format: EcdsaSignatureFormat,
73-
}
74-
75-
/// An ECDSA signing algorithm.
76-
#[derive(Debug, Eq, PartialEq)]
77-
pub struct EcdsaSigningAlgorithm(pub(crate) &'static EcdsaVerificationAlgorithm);
78-
79-
impl Deref for EcdsaSigningAlgorithm {
80-
type Target = EcdsaVerificationAlgorithm;
81-
#[inline]
82-
fn deref(&self) -> &Self::Target {
83-
self.0
84-
}
85-
}
86-
87-
impl sealed::Sealed for EcdsaVerificationAlgorithm {}
88-
impl sealed::Sealed for EcdsaSigningAlgorithm {}
89-
90-
#[derive(Debug, Eq, PartialEq)]
91-
pub(crate) enum EcdsaSignatureFormat {
92-
ASN1,
93-
Fixed,
94-
}
95-
96-
#[derive(Debug, Eq, PartialEq)]
97-
#[allow(non_camel_case_types)]
98-
pub(crate) enum AlgorithmID {
99-
ECDSA_P256,
100-
ECDSA_P384,
101-
ECDSA_P521,
102-
ECDSA_P256K1,
103-
}
104-
105-
impl AlgorithmID {
106-
#[inline]
107-
pub(crate) fn nid(&'static self) -> i32 {
108-
match self {
109-
AlgorithmID::ECDSA_P256 => NID_X9_62_prime256v1,
110-
AlgorithmID::ECDSA_P384 => NID_secp384r1,
111-
AlgorithmID::ECDSA_P521 => NID_secp521r1,
112-
AlgorithmID::ECDSA_P256K1 => NID_secp256k1,
113-
}
114-
}
115-
pub(crate) fn private_key_size(&self) -> usize {
116-
match self {
117-
AlgorithmID::ECDSA_P256 | AlgorithmID::ECDSA_P256K1 => 32,
118-
AlgorithmID::ECDSA_P384 => 48,
119-
AlgorithmID::ECDSA_P521 => 66,
120-
}
121-
}
122-
// Compressed public key length in bytes
123-
#[inline]
124-
const fn compressed_pub_key_len(&self) -> usize {
125-
match self {
126-
AlgorithmID::ECDSA_P256 | AlgorithmID::ECDSA_P256K1 => {
127-
compressed_public_key_size_bytes(256)
128-
}
129-
AlgorithmID::ECDSA_P384 => compressed_public_key_size_bytes(384),
130-
AlgorithmID::ECDSA_P521 => compressed_public_key_size_bytes(521),
131-
}
132-
}
133-
}
134-
135-
/// Elliptic curve public key.
136-
#[derive(Clone)]
137-
pub struct PublicKey {
138-
algorithm: &'static EcdsaSigningAlgorithm,
139-
evp_pkey: LcPtr<EVP_PKEY>,
140-
octets: Box<[u8]>,
141-
}
142-
143-
impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
144-
/// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
145-
/// # Errors
146-
/// Returns an error if the public key fails to marshal to X.509.
147-
fn as_der(&self) -> Result<PublicKeyX509Der<'static>, Unspecified> {
148-
let ec_group = LcPtr::new(unsafe { EC_GROUP_new_by_curve_name(self.algorithm.id.nid()) })?;
149-
let ec_point = ec_point_from_bytes(&ec_group, self.as_ref())?;
150-
let mut ec_key = LcPtr::new(unsafe { EC_KEY_new() })?;
151-
if 1 != unsafe { EC_KEY_set_group(*ec_key.as_mut(), *ec_group.as_const()) } {
152-
return Err(Unspecified);
153-
}
154-
if 1 != unsafe { EC_KEY_set_public_key(*ec_key.as_mut(), *ec_point.as_const()) } {
155-
return Err(Unspecified);
156-
}
157-
let mut buffer = null_mut::<u8>();
158-
let len = unsafe { aws_lc::i2d_EC_PUBKEY(*ec_key.as_const(), &mut buffer) };
159-
if len < 0 || buffer.is_null() {
160-
return Err(Unspecified);
161-
}
162-
let buffer = LcPtr::new(buffer)?;
163-
let der =
164-
unsafe { core::slice::from_raw_parts(*buffer.as_const(), len.try_into()?) }.to_owned();
165-
166-
Ok(PublicKeyX509Der::new(der))
167-
}
168-
}
169-
170-
impl AsBigEndian<EcPublicKeyCompressedBin<'static>> for PublicKey {
171-
/// Provides the public key elliptic curve point to a compressed point bytes format.
172-
/// # Errors
173-
/// Returns an error if the public key fails to marshal.
174-
fn as_be_bytes(&self) -> Result<EcPublicKeyCompressedBin<'static>, crate::error::Unspecified> {
175-
let ec_key = ConstPointer::new(unsafe { EVP_PKEY_get0_EC_KEY(*self.evp_pkey.as_const()) })?;
176-
177-
let mut buffer = vec![0u8; self.algorithm.0.id.compressed_pub_key_len()];
178-
179-
let out_len = marshal_ec_public_key_to_buffer(&mut buffer, &ec_key, true)?;
180-
181-
debug_assert_eq!(buffer.len(), out_len);
182-
183-
buffer.truncate(out_len);
184-
185-
Ok(EcPublicKeyCompressedBin::new(buffer))
186-
}
187-
}
188-
189-
impl AsBigEndian<EcPublicKeyUncompressedBin<'static>> for PublicKey {
190-
/// Provides the public key elliptic curve point to an uncompressed point bytes format.
191-
/// # Errors
192-
/// Returns an error if the public key fails to marshal.
193-
fn as_be_bytes(
194-
&self,
195-
) -> Result<EcPublicKeyUncompressedBin<'static>, crate::error::Unspecified> {
196-
let mut uncompressed_bytes = vec![0u8; self.octets.len()];
197-
uncompressed_bytes.copy_from_slice(&self.octets);
198-
Ok(EcPublicKeyUncompressedBin::new(uncompressed_bytes))
199-
}
200-
}
201-
202-
impl Debug for PublicKey {
203-
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
204-
f.write_str(&format!(
205-
"EcdsaPublicKey(\"{}\")",
206-
hex::encode(self.octets.as_ref())
207-
))
208-
}
209-
}
210-
211-
impl AsRef<[u8]> for PublicKey {
212-
#[inline]
213-
/// Serializes the public key in an uncompressed form (X9.62) using the
214-
/// Octet-String-to-Elliptic-Curve-Point algorithm in
215-
/// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
216-
fn as_ref(&self) -> &[u8] {
217-
self.octets.as_ref()
218-
}
219-
}
220-
221-
unsafe impl Send for PublicKey {}
222-
unsafe impl Sync for PublicKey {}
223-
224-
impl VerificationAlgorithm for EcdsaVerificationAlgorithm {
225-
#[inline]
226-
#[cfg(feature = "ring-sig-verify")]
227-
fn verify(
228-
&self,
229-
public_key: Input<'_>,
230-
msg: Input<'_>,
231-
signature: Input<'_>,
232-
) -> Result<(), Unspecified> {
233-
self.verify_sig(
234-
public_key.as_slice_less_safe(),
235-
msg.as_slice_less_safe(),
236-
signature.as_slice_less_safe(),
237-
)
238-
}
239-
240-
fn verify_sig(
241-
&self,
242-
public_key: &[u8],
243-
msg: &[u8],
244-
signature: &[u8],
245-
) -> Result<(), Unspecified> {
246-
match self.sig_format {
247-
EcdsaSignatureFormat::ASN1 => {
248-
verify_asn1_signature(self.id, self.digest, public_key, msg, signature)
249-
}
250-
EcdsaSignatureFormat::Fixed => {
251-
verify_fixed_signature(self.id, self.digest, public_key, msg, signature)
252-
}
253-
}
254-
}
255-
}
256-
257-
fn verify_fixed_signature(
258-
alg: &'static AlgorithmID,
259-
digest: &'static digest::Algorithm,
260-
public_key: &[u8],
261-
msg: &[u8],
262-
signature: &[u8],
263-
) -> Result<(), Unspecified> {
264-
let mut out_bytes = null_mut::<u8>();
265-
let mut out_bytes_len = MaybeUninit::<usize>::uninit();
266-
let sig = unsafe { ecdsa_sig_from_fixed(alg, signature)? };
267-
if 1 != unsafe {
268-
ECDSA_SIG_to_bytes(&mut out_bytes, out_bytes_len.as_mut_ptr(), *sig.as_const())
269-
} {
270-
return Err(Unspecified);
271-
}
272-
let out_bytes = LcPtr::new(out_bytes)?;
273-
let signature = unsafe { out_bytes.as_slice(out_bytes_len.assume_init()) };
274-
verify_asn1_signature(alg, digest, public_key, msg, signature)
275-
}
276-
277-
fn verify_asn1_signature(
278-
alg: &'static AlgorithmID,
279-
digest: &'static digest::Algorithm,
280-
public_key: &[u8],
281-
msg: &[u8],
282-
signature: &[u8],
283-
) -> Result<(), Unspecified> {
284-
let mut pkey = try_parse_public_key_bytes(public_key, alg.nid())?;
285-
286-
let mut md_ctx = DigestContext::new_uninit();
287-
288-
let digest = digest::match_digest_type(&digest.id);
289-
290-
if 1 != unsafe {
291-
EVP_DigestVerifyInit(
292-
md_ctx.as_mut_ptr(),
293-
null_mut(),
294-
*digest,
295-
null_mut(),
296-
*pkey.as_mut(),
297-
)
298-
} {
299-
return Err(Unspecified);
300-
}
301-
302-
if 1 != indicator_check!(unsafe {
303-
EVP_DigestVerify(
304-
md_ctx.as_mut_ptr(),
305-
signature.as_ptr(),
306-
signature.len(),
307-
msg.as_ptr(),
308-
msg.len(),
309-
)
310-
}) {
311-
return Err(Unspecified);
312-
}
313-
314-
Ok(())
315-
}
316-
31756
fn verify_ec_key_nid(
31857
ec_key: &ConstPointer<EC_KEY>,
31958
expected_curve_nid: i32,
@@ -432,20 +171,6 @@ pub(crate) fn marshal_ec_public_key_to_buffer(
432171
Ok(out_len)
433172
}
434173

435-
pub(crate) fn public_key_from_evp_pkey(
436-
evp_pkey: &LcPtr<EVP_PKEY>,
437-
algorithm: &'static EcdsaSigningAlgorithm,
438-
) -> Result<PublicKey, Unspecified> {
439-
let mut pub_key_bytes = [0u8; PUBLIC_KEY_MAX_LEN];
440-
let key_len = marshal_public_key_to_buffer(&mut pub_key_bytes, evp_pkey, false)?;
441-
442-
Ok(PublicKey {
443-
evp_pkey: evp_pkey.clone(),
444-
algorithm,
445-
octets: pub_key_bytes[0..key_len].into(),
446-
})
447-
}
448-
449174
pub(crate) fn try_parse_public_key_bytes(
450175
key_bytes: &[u8],
451176
expected_curve_nid: i32,
@@ -687,29 +412,6 @@ fn ecdsa_asn1_to_fixed(alg_id: &'static AlgorithmID, sig: &[u8]) -> Result<Signa
687412
}))
688413
}
689414

690-
#[inline]
691-
unsafe fn ecdsa_sig_from_fixed(
692-
alg_id: &'static AlgorithmID,
693-
signature: &[u8],
694-
) -> Result<LcPtr<ECDSA_SIG>, ()> {
695-
let num_size_bytes = alg_id.private_key_size();
696-
if signature.len() != 2 * num_size_bytes {
697-
return Err(());
698-
}
699-
let mut r_bn = DetachableLcPtr::<BIGNUM>::try_from(&signature[..num_size_bytes])?;
700-
let mut s_bn = DetachableLcPtr::<BIGNUM>::try_from(&signature[num_size_bytes..])?;
701-
702-
let mut ecdsa_sig = LcPtr::new(ECDSA_SIG_new())?;
703-
704-
if 1 != ECDSA_SIG_set0(*ecdsa_sig.as_mut(), *r_bn.as_mut(), *s_bn.as_mut()) {
705-
return Err(());
706-
}
707-
r_bn.detach();
708-
s_bn.detach();
709-
710-
Ok(ecdsa_sig)
711-
}
712-
713415
#[inline]
714416
pub(crate) const fn compressed_public_key_size_bytes(curve_field_bits: usize) -> usize {
715417
1 + (curve_field_bits + 7) / 8

aws-lc-rs/src/ec/key_pair.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,12 @@ use core::ptr::{null, null_mut};
1111
use aws_lc::{EVP_DigestSign, EVP_DigestSignInit, EVP_PKEY_get0_EC_KEY, EVP_PKEY};
1212

1313
use crate::digest::digest_ctx::DigestContext;
14+
use crate::ec::evp_key_generate;
15+
use crate::ec::signature::{EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey};
1416
#[cfg(feature = "fips")]
1517
use crate::ec::validate_evp_key;
1618
#[cfg(not(feature = "fips"))]
1719
use crate::ec::verify_evp_key_nid;
18-
use crate::ec::{evp_key_generate, EcdsaSignatureFormat, EcdsaSigningAlgorithm, PublicKey};
1920

2021
use crate::encoding::{AsBigEndian, AsDer, EcPrivateKeyBin, EcPrivateKeyRfc5915Der};
2122
use crate::error::{KeyRejected, Unspecified};
@@ -60,7 +61,7 @@ impl EcdsaKeyPair {
6061
algorithm: &'static EcdsaSigningAlgorithm,
6162
evp_pkey: LcPtr<EVP_PKEY>,
6263
) -> Result<Self, ()> {
63-
let pubkey = ec::public_key_from_evp_pkey(&evp_pkey, algorithm)?;
64+
let pubkey = ec::signature::public_key_from_evp_pkey(&evp_pkey, algorithm)?;
6465

6566
Ok(Self {
6667
algorithm,

0 commit comments

Comments
 (0)