Skip to content

Commit 4ac4434

Browse files
committed
ParsedPublicKey for agreement
1 parent 5aaa88f commit 4ac4434

File tree

2 files changed

+126
-22
lines changed

2 files changed

+126
-22
lines changed

aws-lc-rs/src/agreement.rs

Lines changed: 122 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,11 @@ use crate::ec::encoding::sec1::{
5555
marshal_sec1_private_key, marshal_sec1_public_point, marshal_sec1_public_point_into_buffer,
5656
parse_sec1_private_bn,
5757
};
58+
use crate::ec::evp_key_generate;
5859
#[cfg(feature = "fips")]
5960
use crate::ec::validate_ec_evp_key;
6061
#[cfg(not(feature = "fips"))]
6162
use crate::ec::verify_evp_key_nid;
62-
use crate::ec::{encoding, evp_key_generate};
6363
use crate::error::{KeyRejected, Unspecified};
6464
use crate::hex;
6565
pub use ephemeral::{agree_ephemeral, EphemeralPrivateKey};
@@ -71,6 +71,7 @@ use crate::aws_lc::{
7171

7272
use crate::buffer::Buffer;
7373
use crate::ec;
74+
use crate::ec::encoding::parse_ec_public_key;
7475
use crate::ec::encoding::rfc5915::parse_rfc5915_private_key;
7576
use crate::encoding::{
7677
AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der,
@@ -663,6 +664,118 @@ impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
663664
}
664665
}
665666

667+
#[derive(Debug)]
668+
/// TODO
669+
pub struct ParsedPublicKey {
670+
format: ParsedPublicKeyFormat,
671+
nid: i32,
672+
key: LcPtr<EVP_PKEY>,
673+
}
674+
675+
unsafe impl Send for ParsedPublicKey {}
676+
unsafe impl Sync for ParsedPublicKey {}
677+
678+
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
679+
/// The format of a parsed public key.
680+
///
681+
/// This is used to distinguish between different types of public key formats
682+
/// supported by *aws-lc-rs*.
683+
#[non_exhaustive]
684+
pub enum ParsedPublicKeyFormat {
685+
/// The key is in an X.509 SubjectPublicKeyInfo format.
686+
X509,
687+
/// The key is in an uncompressed form (X9.62).
688+
Uncompressed,
689+
/// The key is in a compressed form (SEC 1: Elliptic Curve Cryptography, Version 2.0).
690+
Compressed,
691+
/// The key is in a raw form.
692+
Raw,
693+
}
694+
695+
/// A parsed public key for key agreement.
696+
impl ParsedPublicKey {
697+
fn nid(&self) -> i32 {
698+
self.nid
699+
}
700+
701+
#[allow(unused)]
702+
fn format(&self) -> ParsedPublicKeyFormat {
703+
self.format
704+
}
705+
706+
pub(crate) fn key(&self) -> &LcPtr<EVP_PKEY> {
707+
&self.key
708+
}
709+
710+
/// The algorithm of the public key.
711+
#[must_use]
712+
#[allow(non_upper_case_globals)]
713+
pub fn alg(&self) -> &'static Algorithm {
714+
match self.nid() {
715+
NID_X25519 => &X25519,
716+
NID_X9_62_prime256v1 => &ECDH_P256,
717+
NID_secp384r1 => &ECDH_P384,
718+
NID_secp521r1 => &ECDH_P521,
719+
_ => unreachable!("Unreachable agreement algorithm nid: {}", self.nid()),
720+
}
721+
}
722+
}
723+
724+
impl ParsedPublicKey {
725+
#[allow(non_upper_case_globals)]
726+
pub(crate) fn new(bytes: impl AsRef<[u8]>, nid: i32) -> Result<Self, KeyRejected> {
727+
let key_bytes = bytes.as_ref();
728+
if key_bytes.is_empty() {
729+
return Err(KeyRejected::unspecified());
730+
}
731+
let format = match key_bytes[0] {
732+
0x04 => ParsedPublicKeyFormat::Uncompressed,
733+
0x02 | 0x03 => ParsedPublicKeyFormat::Compressed,
734+
_ => ParsedPublicKeyFormat::X509,
735+
};
736+
match nid {
737+
NID_X25519 => {
738+
let pub_key = try_parse_x25519_public_key_bytes(key_bytes)?;
739+
Ok(ParsedPublicKey {
740+
format,
741+
nid,
742+
key: pub_key,
743+
})
744+
}
745+
NID_X9_62_prime256v1 | NID_secp384r1 | NID_secp521r1 => {
746+
let pub_key = parse_ec_public_key(key_bytes, nid)?;
747+
Ok(ParsedPublicKey {
748+
format,
749+
nid,
750+
key: pub_key,
751+
})
752+
}
753+
_ => Err(KeyRejected::unspecified()),
754+
}
755+
}
756+
}
757+
758+
impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
759+
#[allow(dead_code)]
760+
fn parse(&self) -> Result<ParsedPublicKey, KeyRejected> {
761+
ParsedPublicKey::new(&self.bytes, self.alg.id.nid())
762+
}
763+
}
764+
765+
impl<B: AsRef<[u8]>> TryFrom<&UnparsedPublicKey<B>> for ParsedPublicKey {
766+
type Error = KeyRejected;
767+
fn try_from(upk: &UnparsedPublicKey<B>) -> Result<Self, Self::Error> {
768+
upk.parse()
769+
}
770+
}
771+
772+
impl<B: AsRef<[u8]>> TryFrom<UnparsedPublicKey<B>> for ParsedPublicKey {
773+
type Error = KeyRejected;
774+
fn try_from(upk: UnparsedPublicKey<B>) -> Result<Self, Self::Error> {
775+
upk.parse()
776+
}
777+
}
778+
666779
/// Performs a key agreement with a private key and the given public key.
667780
///
668781
/// `my_private_key` is the private key to use. Only a reference to the key
@@ -692,36 +805,27 @@ impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
692805
/// `error_value` on internal failure.
693806
#[inline]
694807
#[allow(clippy::missing_panics_doc)]
695-
pub fn agree<B: AsRef<[u8]>, F, R, E>(
808+
pub fn agree<B: TryInto<ParsedPublicKey>, F, R, E>(
696809
my_private_key: &PrivateKey,
697-
peer_public_key: &UnparsedPublicKey<B>,
810+
peer_public_key: B,
698811
error_value: E,
699812
kdf: F,
700813
) -> Result<R, E>
701814
where
702815
F: FnOnce(&[u8]) -> Result<R, E>,
703816
{
704817
let expected_alg = my_private_key.algorithm();
705-
let expected_nid = expected_alg.id.nid();
706-
707-
if peer_public_key.alg != expected_alg {
708-
return Err(error_value);
709-
}
710818

711-
let peer_pub_bytes = peer_public_key.bytes.as_ref();
712-
713-
let parse_result = match &my_private_key.inner_key {
714-
KeyInner::X25519(_) => try_parse_x25519_public_key_bytes(peer_pub_bytes),
715-
KeyInner::ECDH_P256(_) | KeyInner::ECDH_P384(_) | KeyInner::ECDH_P521(_) => {
716-
encoding::parse_ec_public_key(peer_pub_bytes, expected_nid)
717-
}
718-
};
819+
let parse_result = peer_public_key.try_into();
719820

720821
if let Ok(peer_pub_key) = parse_result {
822+
if peer_pub_key.alg() != expected_alg {
823+
return Err(error_value);
824+
}
721825
let secret = my_private_key
722826
.inner_key
723827
.get_evp_pkey()
724-
.agree(&peer_pub_key)
828+
.agree(peer_pub_key.key())
725829
.or(Err(error_value))?;
726830

727831
kdf(secret.as_ref())
@@ -824,7 +928,7 @@ mod tests {
824928
assert!(PrivateKey::from_private_key_der(alg, test_key).is_err());
825929
assert!(agree(
826930
my_private_key,
827-
&UnparsedPublicKey::new(alg, test_key),
931+
UnparsedPublicKey::new(alg, test_key),
828932
(),
829933
|_| Ok(())
830934
)

aws-lc-rs/src/agreement/ephemeral.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0 OR ISC
33

4-
use crate::agreement::{agree, Algorithm, PrivateKey, PublicKey, UnparsedPublicKey};
4+
use crate::agreement::{agree, Algorithm, ParsedPublicKey, PrivateKey, PublicKey};
55
use crate::error::Unspecified;
66
use crate::rand::SecureRandom;
77
use core::fmt;
@@ -98,9 +98,9 @@ impl EphemeralPrivateKey {
9898
#[allow(clippy::needless_pass_by_value)]
9999
#[allow(clippy::missing_panics_doc)]
100100
#[allow(clippy::module_name_repetitions)]
101-
pub fn agree_ephemeral<B: AsRef<[u8]>, F, R, E>(
101+
pub fn agree_ephemeral<B: TryInto<ParsedPublicKey>, F, R, E>(
102102
my_private_key: EphemeralPrivateKey,
103-
peer_public_key: &UnparsedPublicKey<B>,
103+
peer_public_key: B,
104104
error_value: E,
105105
kdf: F,
106106
) -> Result<R, E>
@@ -496,7 +496,7 @@ mod tests {
496496
let private_key =
497497
agreement::EphemeralPrivateKey::generate_for_test(&agreement::X25519, &rng)?;
498498
let public_key = agreement::UnparsedPublicKey::new(&agreement::X25519, public_key);
499-
agreement::agree_ephemeral(private_key, &public_key, Unspecified, |agreed_value| {
499+
agreement::agree_ephemeral(private_key, public_key, Unspecified, |agreed_value| {
500500
Ok(Vec::from(agreed_value))
501501
})
502502
}

0 commit comments

Comments
 (0)