blob: c972a9406a4376db3154c43998879f4655f262be [file] [log] [blame] [edit]
// Copyright 2015-2017 Brian Smith.
// SPDX-License-Identifier: ISC
// Modifications copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0 OR ISC
//! Key Agreement: ECDH, including X25519.
//!
//! # Example
//!
//! Note that this example uses X25519, but ECDH using NIST P-256/P-384 is done
//! exactly the same way, just substituting
//! `agreement::ECDH_P256`/`agreement::ECDH_P384` for `agreement::X25519`.
//!
//! ```
//! use aws_lc_rs::{agreement, rand};
//!
//! let rng = rand::SystemRandom::new();
//!
//! let my_private_key = agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
//!
//! // Make `my_public_key` a byte slice containing my public key. In a real
//! // application, this would be sent to the peer in an encoded protocol
//! // message.
//! let my_public_key = my_private_key.compute_public_key()?;
//!
//! let peer_public_key = {
//! // In a real application, the peer public key would be parsed out of a
//! // protocol message. Here we just generate one.
//! let peer_public_key = {
//! let peer_private_key =
//! agreement::EphemeralPrivateKey::generate(&agreement::X25519, &rng)?;
//! peer_private_key.compute_public_key()?
//! };
//!
//! agreement::UnparsedPublicKey::new(&agreement::X25519, peer_public_key)
//! };
//!
//! agreement::agree_ephemeral(
//! my_private_key,
//! &peer_public_key,
//! aws_lc_rs::error::Unspecified,
//! |_key_material| {
//! // In a real application, we'd apply a KDF to the key material and the
//! // public keys (as recommended in RFC 7748) and then derive session
//! // keys from the result. We omit all that here.
//! Ok(())
//! },
//! )?;
//!
//! # Ok::<(), aws_lc_rs::error::Unspecified>(())
//! ```
mod ephemeral;
use crate::ec::encoding::sec1::{
marshal_sec1_private_key, marshal_sec1_public_point, marshal_sec1_public_point_into_buffer,
parse_sec1_private_bn, parse_sec1_public_point,
};
#[cfg(not(feature = "fips"))]
use crate::ec::verify_evp_key_nid;
use crate::ec::{evp_key_generate, validate_ec_evp_key};
use crate::error::{KeyRejected, Unspecified};
use crate::hex;
pub use ephemeral::{agree_ephemeral, EphemeralPrivateKey};
use crate::aws_lc::{
i2d_ECPrivateKey, EVP_PKEY_get0_EC_KEY, NID_X9_62_prime256v1, NID_secp384r1, NID_secp521r1,
EVP_PKEY, EVP_PKEY_EC, EVP_PKEY_X25519, NID_X25519,
};
use crate::buffer::Buffer;
use crate::ec;
use crate::ec::encoding::rfc5915::parse_rfc5915_private_key;
use crate::encoding::{
AsBigEndian, AsDer, Curve25519SeedBin, EcPrivateKeyBin, EcPrivateKeyRfc5915Der,
EcPublicKeyCompressedBin, EcPublicKeyUncompressedBin, Pkcs8V1Der, PublicKeyX509Der,
};
use crate::evp_pkey::No_EVP_PKEY_CTX_consumer;
use crate::pkcs8::Version;
use crate::ptr::LcPtr;
use core::fmt;
use core::fmt::{Debug, Formatter};
use core::ptr::null_mut;
#[allow(non_camel_case_types)]
#[derive(PartialEq, Eq)]
enum AlgorithmID {
ECDH_P256,
ECDH_P384,
ECDH_P521,
X25519,
}
impl AlgorithmID {
#[inline]
const fn nid(&self) -> i32 {
match self {
AlgorithmID::ECDH_P256 => NID_X9_62_prime256v1,
AlgorithmID::ECDH_P384 => NID_secp384r1,
AlgorithmID::ECDH_P521 => NID_secp521r1,
AlgorithmID::X25519 => NID_X25519,
}
}
// Uncompressed public key length in bytes
#[inline]
const fn pub_key_len(&self) -> usize {
match self {
AlgorithmID::ECDH_P256 => ec::uncompressed_public_key_size_bytes(256),
AlgorithmID::ECDH_P384 => ec::uncompressed_public_key_size_bytes(384),
AlgorithmID::ECDH_P521 => ec::uncompressed_public_key_size_bytes(521),
AlgorithmID::X25519 => 32,
}
}
#[inline]
const fn private_key_len(&self) -> usize {
match self {
AlgorithmID::ECDH_P256 | AlgorithmID::X25519 => 32,
AlgorithmID::ECDH_P384 => 48,
AlgorithmID::ECDH_P521 => 66,
}
}
}
impl Debug for AlgorithmID {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
let output = match self {
AlgorithmID::ECDH_P256 => "curve: P256",
AlgorithmID::ECDH_P384 => "curve: P384",
AlgorithmID::ECDH_P521 => "curve: P521",
AlgorithmID::X25519 => "curve: Curve25519",
};
f.write_str(output)
}
}
/// A key agreement algorithm.
#[derive(PartialEq, Eq)]
pub struct Algorithm {
id: AlgorithmID,
}
impl Debug for Algorithm {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(&format!("Algorithm {{ {:?} }}", self.id))
}
}
/// ECDH using the NSA Suite B P-256 (secp256r1) curve.
pub const ECDH_P256: Algorithm = Algorithm {
id: AlgorithmID::ECDH_P256,
};
/// ECDH using the NSA Suite B P-384 (secp384r1) curve.
pub const ECDH_P384: Algorithm = Algorithm {
id: AlgorithmID::ECDH_P384,
};
/// ECDH using the NSA Suite B P-521 (secp521r1) curve.
pub const ECDH_P521: Algorithm = Algorithm {
id: AlgorithmID::ECDH_P521,
};
/// X25519 (ECDH using Curve25519) as described in [RFC 7748].
///
/// Everything is as described in RFC 7748. Key agreement will fail if the
/// result of the X25519 operation is zero; see the notes on the
/// "all-zero value" in [RFC 7748 section 6.1].
///
/// [RFC 7748]: https://tools.ietf.org/html/rfc7748
/// [RFC 7748 section 6.1]: https://tools.ietf.org/html/rfc7748#section-6.1
pub const X25519: Algorithm = Algorithm {
id: AlgorithmID::X25519,
};
#[allow(non_camel_case_types)]
enum KeyInner {
ECDH_P256(LcPtr<EVP_PKEY>),
ECDH_P384(LcPtr<EVP_PKEY>),
ECDH_P521(LcPtr<EVP_PKEY>),
X25519(LcPtr<EVP_PKEY>),
}
impl Clone for KeyInner {
fn clone(&self) -> KeyInner {
match self {
KeyInner::ECDH_P256(evp_pkey) => KeyInner::ECDH_P256(evp_pkey.clone()),
KeyInner::ECDH_P384(evp_pkey) => KeyInner::ECDH_P384(evp_pkey.clone()),
KeyInner::ECDH_P521(evp_pkey) => KeyInner::ECDH_P521(evp_pkey.clone()),
KeyInner::X25519(evp_pkey) => KeyInner::X25519(evp_pkey.clone()),
}
}
}
/// A private key for use (only) with `agree`. The
/// signature of `agree` allows `PrivateKey` to be
/// used for more than one key agreement.
pub struct PrivateKey {
inner_key: KeyInner,
}
impl KeyInner {
#[inline]
fn algorithm(&self) -> &'static Algorithm {
match self {
KeyInner::ECDH_P256(..) => &ECDH_P256,
KeyInner::ECDH_P384(..) => &ECDH_P384,
KeyInner::ECDH_P521(..) => &ECDH_P521,
KeyInner::X25519(..) => &X25519,
}
}
fn get_evp_pkey(&self) -> &LcPtr<EVP_PKEY> {
match self {
KeyInner::ECDH_P256(evp_pkey)
| KeyInner::ECDH_P384(evp_pkey)
| KeyInner::ECDH_P521(evp_pkey)
| KeyInner::X25519(evp_pkey) => evp_pkey,
}
}
}
// See EVP_PKEY documentation here:
// https://github.com/aws/aws-lc/blob/125af14c57451565b875fbf1282a38a6ecf83782/include/openssl/evp.h#L83-L89
// An |EVP_PKEY| object represents a public or private key. A given object may
// be used concurrently on multiple threads by non-mutating functions, provided
// no other thread is concurrently calling a mutating function. Unless otherwise
// documented, functions which take a |const| pointer are non-mutating and
// functions which take a non-|const| pointer are mutating.
unsafe impl Send for PrivateKey {}
unsafe impl Sync for PrivateKey {}
impl Debug for PrivateKey {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(&format!(
"PrivateKey {{ algorithm: {:?} }}",
self.inner_key.algorithm()
))
}
}
impl PrivateKey {
fn new(alg: &'static Algorithm, evp_pkey: LcPtr<EVP_PKEY>) -> Self {
match alg.id {
AlgorithmID::X25519 => Self {
inner_key: KeyInner::X25519(evp_pkey),
},
AlgorithmID::ECDH_P256 => Self {
inner_key: KeyInner::ECDH_P256(evp_pkey),
},
AlgorithmID::ECDH_P384 => Self {
inner_key: KeyInner::ECDH_P384(evp_pkey),
},
AlgorithmID::ECDH_P521 => Self {
inner_key: KeyInner::ECDH_P521(evp_pkey),
},
}
}
#[inline]
/// Generate a new private key for the given algorithm.
// # FIPS
// Use this function with one of the following algorithms:
// * `ECDH_P256`
// * `ECDH_P384`
// * `ECDH_P521`
//
/// # Errors
/// `error::Unspecified` when operation fails due to internal error.
pub fn generate(alg: &'static Algorithm) -> Result<Self, Unspecified> {
let evp_pkey = match alg.id {
AlgorithmID::X25519 => generate_x25519()?,
_ => evp_key_generate(alg.id.nid())?,
};
Ok(Self::new(alg, evp_pkey))
}
/// Deserializes a DER-encoded private key structure to produce a `agreement::PrivateKey`.
///
/// This function is typically used to deserialize RFC 5915 encoded private keys, but it will
/// attempt to automatically detect other key formats. This function supports unencrypted
/// PKCS#8 `PrivateKeyInfo` structures as well as key type specific formats.
///
/// X25519 keys are not supported. See `PrivateKey::as_der`.
///
/// # Errors
/// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
///
/// # Panics
pub fn from_private_key_der(
alg: &'static Algorithm,
key_bytes: &[u8],
) -> Result<Self, KeyRejected> {
if AlgorithmID::X25519 == alg.id {
return Err(KeyRejected::invalid_encoding());
}
let evp_pkey = LcPtr::<EVP_PKEY>::parse_rfc5208_private_key(key_bytes, EVP_PKEY_EC)
.or(parse_rfc5915_private_key(key_bytes, alg.id.nid()))?;
#[cfg(not(feature = "fips"))]
verify_evp_key_nid(&evp_pkey.as_const(), alg.id.nid())?;
#[cfg(feature = "fips")]
validate_ec_evp_key(&evp_pkey.as_const(), alg.id.nid())?;
Ok(Self::new(alg, evp_pkey))
}
/// Constructs an ECDH key from private key bytes
///
/// The private key must encoded as a big-endian fixed-length integer. For
/// example, a P-256 private key must be 32 bytes prefixed with leading
/// zeros as needed.
///
/// # Errors
/// `error::KeyRejected` if parsing failed or key otherwise unacceptable.
pub fn from_private_key(
alg: &'static Algorithm,
key_bytes: &[u8],
) -> Result<Self, KeyRejected> {
if key_bytes.len() != alg.id.private_key_len() {
return Err(KeyRejected::wrong_algorithm());
}
let evp_pkey = if AlgorithmID::X25519 == alg.id {
LcPtr::<EVP_PKEY>::parse_raw_private_key(key_bytes, EVP_PKEY_X25519)?
} else {
parse_sec1_private_bn(key_bytes, alg.id.nid())?
};
Ok(Self::new(alg, evp_pkey))
}
#[cfg(test)]
#[allow(missing_docs, clippy::missing_errors_doc)]
pub fn generate_for_test(
alg: &'static Algorithm,
rng: &dyn crate::rand::SecureRandom,
) -> Result<Self, Unspecified> {
match alg.id {
AlgorithmID::X25519 => {
let mut priv_key = [0u8; AlgorithmID::X25519.private_key_len()];
rng.fill(&mut priv_key)?;
Self::from_x25519_private_key(&priv_key)
}
AlgorithmID::ECDH_P256 => {
let mut priv_key = [0u8; AlgorithmID::ECDH_P256.private_key_len()];
rng.fill(&mut priv_key)?;
Self::from_p256_private_key(&priv_key)
}
AlgorithmID::ECDH_P384 => {
let mut priv_key = [0u8; AlgorithmID::ECDH_P384.private_key_len()];
rng.fill(&mut priv_key)?;
Self::from_p384_private_key(&priv_key)
}
AlgorithmID::ECDH_P521 => {
let mut priv_key = [0u8; AlgorithmID::ECDH_P521.private_key_len()];
rng.fill(&mut priv_key)?;
Self::from_p521_private_key(&priv_key)
}
}
}
#[cfg(test)]
fn from_x25519_private_key(
priv_key: &[u8; AlgorithmID::X25519.private_key_len()],
) -> Result<Self, Unspecified> {
let pkey = LcPtr::<EVP_PKEY>::parse_raw_private_key(priv_key, EVP_PKEY_X25519)?;
Ok(PrivateKey {
inner_key: KeyInner::X25519(pkey),
})
}
#[cfg(test)]
fn from_p256_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
let pkey = parse_sec1_private_bn(priv_key, ECDH_P256.id.nid())?;
Ok(PrivateKey {
inner_key: KeyInner::ECDH_P256(pkey),
})
}
#[cfg(test)]
fn from_p384_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
let pkey = parse_sec1_private_bn(priv_key, ECDH_P384.id.nid())?;
Ok(PrivateKey {
inner_key: KeyInner::ECDH_P384(pkey),
})
}
#[cfg(test)]
fn from_p521_private_key(priv_key: &[u8]) -> Result<Self, Unspecified> {
let pkey = parse_sec1_private_bn(priv_key, ECDH_P521.id.nid())?;
Ok(PrivateKey {
inner_key: KeyInner::ECDH_P521(pkey),
})
}
/// Computes the public key from the private key.
///
/// # Errors
/// `error::Unspecified` when operation fails due to internal error.
pub fn compute_public_key(&self) -> Result<PublicKey, Unspecified> {
match &self.inner_key {
KeyInner::ECDH_P256(evp_pkey)
| KeyInner::ECDH_P384(evp_pkey)
| KeyInner::ECDH_P521(evp_pkey) => {
let mut public_key = [0u8; MAX_PUBLIC_KEY_LEN];
let len = marshal_sec1_public_point_into_buffer(&mut public_key, evp_pkey, false)?;
Ok(PublicKey {
inner_key: self.inner_key.clone(),
key_bytes: public_key,
len,
})
}
KeyInner::X25519(priv_key) => {
let mut buffer = [0u8; MAX_PUBLIC_KEY_LEN];
let out_len = priv_key
.as_const()
.marshal_raw_public_to_buffer(&mut buffer)?;
Ok(PublicKey {
inner_key: self.inner_key.clone(),
key_bytes: buffer,
len: out_len,
})
}
}
}
/// The algorithm for the private key.
#[inline]
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.inner_key.algorithm()
}
}
impl AsDer<EcPrivateKeyRfc5915Der<'static>> for PrivateKey {
/// Serializes the key as a DER-encoded `ECPrivateKey` (RFC 5915) structure.
///
/// X25519 is not supported.
///
/// # Errors
/// `error::Unspecified` if serialization failed.
fn as_der(&self) -> Result<EcPrivateKeyRfc5915Der<'static>, Unspecified> {
if AlgorithmID::X25519 == self.inner_key.algorithm().id {
return Err(Unspecified);
}
let mut outp = null_mut::<u8>();
let ec_key = {
self.inner_key
.get_evp_pkey()
.project_const_lifetime(unsafe {
|evp_pkey| EVP_PKEY_get0_EC_KEY(*evp_pkey.as_const())
})?
};
let length = usize::try_from(unsafe { i2d_ECPrivateKey(*ec_key, &mut outp) })
.map_err(|_| Unspecified)?;
let mut outp = LcPtr::new(outp)?;
Ok(EcPrivateKeyRfc5915Der::take_from_slice(unsafe {
core::slice::from_raw_parts_mut(*outp.as_mut(), length)
}))
}
}
impl AsDer<Pkcs8V1Der<'static>> for PrivateKey {
/// Serializes the key as a PKCS #8 private key structure.
///
/// X25519 is not supported.
///
/// # Errors
/// `error::Unspecified` if serialization failed.
fn as_der(&self) -> Result<Pkcs8V1Der<'static>, Unspecified> {
if AlgorithmID::X25519 == self.inner_key.algorithm().id {
return Err(Unspecified);
}
Ok(Pkcs8V1Der::new(
self.inner_key
.get_evp_pkey()
.as_const()
.marshal_rfc5208_private_key(Version::V1)?,
))
}
}
impl AsBigEndian<EcPrivateKeyBin<'static>> for PrivateKey {
/// Exposes the private key encoded as a big-endian fixed-length integer.
///
/// X25519 is not supported.
///
/// # Errors
/// `error::Unspecified` if serialization failed.
fn as_be_bytes(&self) -> Result<EcPrivateKeyBin<'static>, Unspecified> {
if AlgorithmID::X25519 == self.inner_key.algorithm().id {
return Err(Unspecified);
}
let buffer = marshal_sec1_private_key(self.inner_key.get_evp_pkey())?;
Ok(EcPrivateKeyBin::new(buffer))
}
}
impl AsBigEndian<Curve25519SeedBin<'static>> for PrivateKey {
/// Exposes the seed encoded as a big-endian fixed-length integer.
///
/// Only X25519 is supported.
///
/// # Errors
/// `error::Unspecified` if serialization failed.
fn as_be_bytes(&self) -> Result<Curve25519SeedBin<'static>, Unspecified> {
if AlgorithmID::X25519 != self.inner_key.algorithm().id {
return Err(Unspecified);
}
let evp_pkey = self.inner_key.get_evp_pkey();
Ok(Curve25519SeedBin::new(
evp_pkey.as_const().marshal_raw_private_key()?,
))
}
}
pub(crate) fn generate_x25519() -> Result<LcPtr<EVP_PKEY>, Unspecified> {
LcPtr::<EVP_PKEY>::generate(EVP_PKEY_X25519, No_EVP_PKEY_CTX_consumer)
}
const MAX_PUBLIC_KEY_LEN: usize = ec::PUBLIC_KEY_MAX_LEN;
/// A public key for key agreement.
pub struct PublicKey {
inner_key: KeyInner,
key_bytes: [u8; MAX_PUBLIC_KEY_LEN],
len: usize,
}
impl PublicKey {
/// The algorithm for the public key.
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.inner_key.algorithm()
}
}
// See EVP_PKEY documentation here:
// https://github.com/aws/aws-lc/blob/125af14c57451565b875fbf1282a38a6ecf83782/include/openssl/evp.h#L83-L89
// An |EVP_PKEY| object represents a public or private key. A given object may
// be used concurrently on multiple threads by non-mutating functions, provided
// no other thread is concurrently calling a mutating function. Unless otherwise
// documented, functions which take a |const| pointer are non-mutating and
// functions which take a non-|const| pointer are mutating.
unsafe impl Send for PublicKey {}
unsafe impl Sync for PublicKey {}
impl Debug for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
f.write_str(&format!(
"PublicKey {{ algorithm: {:?}, bytes: \"{}\" }}",
self.inner_key.algorithm(),
hex::encode(&self.key_bytes[0..self.len])
))
}
}
impl AsRef<[u8]> for PublicKey {
/// Serializes the public key in an uncompressed form (X9.62) using the
/// Octet-String-to-Elliptic-Curve-Point algorithm in
/// [SEC 1: Elliptic Curve Cryptography, Version 2.0].
fn as_ref(&self) -> &[u8] {
&self.key_bytes[0..self.len]
}
}
impl Clone for PublicKey {
fn clone(&self) -> Self {
PublicKey {
inner_key: self.inner_key.clone(),
key_bytes: self.key_bytes,
len: self.len,
}
}
}
impl AsDer<PublicKeyX509Der<'static>> for PublicKey {
/// Provides the public key as a DER-encoded (X.509) `SubjectPublicKeyInfo` structure.
/// # Errors
/// Returns an error if the public key fails to marshal to X.509.
fn as_der(&self) -> Result<PublicKeyX509Der<'static>, crate::error::Unspecified> {
match &self.inner_key {
KeyInner::ECDH_P256(evp_pkey)
| KeyInner::ECDH_P384(evp_pkey)
| KeyInner::ECDH_P521(evp_pkey)
| KeyInner::X25519(evp_pkey) => {
let der = evp_pkey.as_const().marshal_rfc5280_public_key()?;
Ok(PublicKeyX509Der::from(Buffer::new(der)))
}
}
}
}
impl AsBigEndian<EcPublicKeyCompressedBin<'static>> for PublicKey {
/// Provides the public key elliptic curve point to a compressed point format.
/// # Errors
/// Returns an error if the underlying implementation is unable to marshal the public key to this format.
fn as_be_bytes(&self) -> Result<EcPublicKeyCompressedBin<'static>, crate::error::Unspecified> {
let evp_pkey = match &self.inner_key {
KeyInner::ECDH_P256(evp_pkey)
| KeyInner::ECDH_P384(evp_pkey)
| KeyInner::ECDH_P521(evp_pkey) => evp_pkey,
KeyInner::X25519(_) => return Err(Unspecified),
};
let pub_point = marshal_sec1_public_point(evp_pkey, true)?;
Ok(EcPublicKeyCompressedBin::new(pub_point))
}
}
impl AsBigEndian<EcPublicKeyUncompressedBin<'static>> for PublicKey {
/// Provides the public key elliptic curve point to a compressed point format.
///
/// Equivalent to [`PublicKey::as_ref`] for ECDH key types, except that it provides you a copy instead of a reference.
///
/// # Errors
/// Returns an error if the underlying implementation is unable to marshal the public key to this format.
fn as_be_bytes(
&self,
) -> Result<EcPublicKeyUncompressedBin<'static>, crate::error::Unspecified> {
if self.algorithm().id == AlgorithmID::X25519 {
return Err(Unspecified);
}
let mut buffer = vec![0u8; self.len];
buffer.copy_from_slice(&self.key_bytes[0..self.len]);
Ok(EcPublicKeyUncompressedBin::new(buffer))
}
}
/// An unparsed, possibly malformed, public key for key agreement.
#[derive(Clone)]
pub struct UnparsedPublicKey<B: AsRef<[u8]>> {
alg: &'static Algorithm,
bytes: B,
}
impl<B: Copy + AsRef<[u8]>> Copy for UnparsedPublicKey<B> {}
impl<B: Debug + AsRef<[u8]>> Debug for UnparsedPublicKey<B> {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
f.write_str(&format!(
"UnparsedPublicKey {{ algorithm: {:?}, bytes: {:?} }}",
self.alg,
hex::encode(self.bytes.as_ref())
))
}
}
impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
/// Constructs a new `UnparsedPublicKey`.
pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
UnparsedPublicKey {
alg: algorithm,
bytes,
}
}
/// The agreement algorithm associated with this public key
pub fn algorithm(&self) -> &'static Algorithm {
self.alg
}
/// The bytes provided for this public key
pub fn bytes(&self) -> &B {
&self.bytes
}
}
/// A parsed public key for key agreement.
///
/// This represents a public key that has been successfully parsed and validated
/// from its encoded form. The key can be used with the `agree` function to
/// perform key agreement operations.
#[derive(Debug, Clone)]
pub struct ParsedPublicKey {
format: ParsedPublicKeyFormat,
nid: i32,
key: LcPtr<EVP_PKEY>,
bytes: Box<[u8]>,
}
// See EVP_PKEY documentation here:
// https://github.com/aws/aws-lc/blob/125af14c57451565b875fbf1282a38a6ecf83782/include/openssl/evp.h#L83-L89
// An |EVP_PKEY| object represents a public or private key. A given object may
// be used concurrently on multiple threads by non-mutating functions, provided
// no other thread is concurrently calling a mutating function. Unless otherwise
// documented, functions which take a |const| pointer are non-mutating and
// functions which take a non-|const| pointer are mutating.
unsafe impl Send for ParsedPublicKey {}
unsafe impl Sync for ParsedPublicKey {}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
/// The format of a parsed public key.
///
/// This is used to distinguish between different types of public key formats
/// supported by *aws-lc-rs*.
#[non_exhaustive]
pub enum ParsedPublicKeyFormat {
/// The key is in an X.509 SubjectPublicKeyInfo format.
X509,
/// The key is in an uncompressed form (X9.62).
Uncompressed,
/// The key is in a compressed form (SEC 1: Elliptic Curve Cryptography, Version 2.0).
Compressed,
/// The key is in a hybrid form (SEC 1: Elliptic Curve Cryptography, Version 2.0).
Hybrid,
/// The key is in a raw form. (X25519 only)
Raw,
/// The key is in an unknown format.
Unknown,
}
/// A parsed public key for key agreement.
impl ParsedPublicKey {
fn nid(&self) -> i32 {
self.nid
}
/// The format of the data the public key was parsed from.
#[must_use]
pub fn format(&self) -> ParsedPublicKeyFormat {
self.format
}
pub(crate) fn key(&self) -> &LcPtr<EVP_PKEY> {
&self.key
}
/// The algorithm of the public key.
#[must_use]
#[allow(non_upper_case_globals)]
pub fn alg(&self) -> &'static Algorithm {
match self.nid() {
NID_X25519 => &X25519,
NID_X9_62_prime256v1 => &ECDH_P256,
NID_secp384r1 => &ECDH_P384,
NID_secp521r1 => &ECDH_P521,
_ => unreachable!("Unreachable agreement algorithm nid: {}", self.nid()),
}
}
}
impl ParsedPublicKey {
#[allow(non_upper_case_globals)]
pub(crate) fn new(bytes: impl AsRef<[u8]>, nid: i32) -> Result<Self, KeyRejected> {
let bytes = bytes.as_ref().to_vec().into_boxed_slice();
if bytes.is_empty() {
return Err(KeyRejected::unspecified());
}
match nid {
NID_X25519 => {
let format: ParsedPublicKeyFormat;
let key = if let Ok(evp_pkey) =
LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(&bytes, EVP_PKEY_X25519)
{
format = ParsedPublicKeyFormat::X509;
evp_pkey
} else {
format = ParsedPublicKeyFormat::Raw;
try_parse_x25519_public_key_raw_bytes(&bytes)?
};
Ok(ParsedPublicKey {
format,
nid,
key,
bytes,
})
}
NID_X9_62_prime256v1 | NID_secp384r1 | NID_secp521r1 => {
let format: ParsedPublicKeyFormat;
let key = if let Ok(evp_pkey) =
LcPtr::<EVP_PKEY>::parse_rfc5280_public_key(&bytes, EVP_PKEY_EC)
{
validate_ec_evp_key(&evp_pkey.as_const(), nid)?;
format = ParsedPublicKeyFormat::X509;
evp_pkey
} else if let Ok(evp_pkey) = parse_sec1_public_point(&bytes, nid) {
format = match bytes[0] {
0x02 | 0x03 => ParsedPublicKeyFormat::Compressed,
0x04 => ParsedPublicKeyFormat::Uncompressed,
0x06 | 0x07 => ParsedPublicKeyFormat::Hybrid,
_ => ParsedPublicKeyFormat::Unknown,
};
evp_pkey
} else {
return Err(KeyRejected::invalid_encoding());
};
Ok(ParsedPublicKey {
format,
nid,
key,
bytes,
})
}
_ => Err(KeyRejected::unspecified()),
}
}
}
impl AsRef<[u8]> for ParsedPublicKey {
/// Returns the original bytes from which this key was parsed.
fn as_ref(&self) -> &[u8] {
&self.bytes
}
}
impl<B: AsRef<[u8]>> UnparsedPublicKey<B> {
#[allow(dead_code)]
fn parse(&self) -> Result<ParsedPublicKey, KeyRejected> {
ParsedPublicKey::new(&self.bytes, self.alg.id.nid())
}
}
impl<B: AsRef<[u8]>> TryFrom<&UnparsedPublicKey<B>> for ParsedPublicKey {
type Error = KeyRejected;
fn try_from(upk: &UnparsedPublicKey<B>) -> Result<Self, Self::Error> {
upk.parse()
}
}
impl<B: AsRef<[u8]>> TryFrom<UnparsedPublicKey<B>> for ParsedPublicKey {
type Error = KeyRejected;
fn try_from(upk: UnparsedPublicKey<B>) -> Result<Self, Self::Error> {
upk.parse()
}
}
/// Performs a key agreement with a private key and the given public key.
///
/// `my_private_key` is the private key to use. Only a reference to the key
/// is required, allowing the key to continue to be used.
///
/// `peer_public_key` is the peer's public key. `agree` will return
/// `Err(error_value)` if it does not match `my_private_key's` algorithm/curve.
/// `agree` verifies that it is encoded in the standard form for the
/// algorithm and that the key is *valid*; see the algorithm's documentation for
/// details on how keys are to be encoded and what constitutes a valid key for
/// that algorithm.
///
/// `error_value` is the value to return if an error occurs before `kdf` is
/// called, e.g. when decoding of the peer's public key fails or when the public
/// key is otherwise invalid.
///
/// After the key agreement is done, `agree` calls `kdf` with the raw
/// key material from the key agreement operation and then returns what `kdf`
/// returns.
// # FIPS
// Use this function with one of the following key algorithms:
// * `ECDH_P256`
// * `ECDH_P384`
// * `ECDH_P521`
//
/// # Errors
/// `error_value` on internal failure.
#[inline]
#[allow(clippy::missing_panics_doc)]
pub fn agree<B: TryInto<ParsedPublicKey>, F, R, E>(
my_private_key: &PrivateKey,
peer_public_key: B,
error_value: E,
kdf: F,
) -> Result<R, E>
where
F: FnOnce(&[u8]) -> Result<R, E>,
{
let expected_alg = my_private_key.algorithm();
let parse_result = peer_public_key.try_into();
if let Ok(peer_pub_key) = parse_result {
if peer_pub_key.alg() != expected_alg {
return Err(error_value);
}
let secret = my_private_key
.inner_key
.get_evp_pkey()
.agree(peer_pub_key.key())
.or(Err(error_value))?;
kdf(secret.as_ref())
} else {
Err(error_value)
}
}
fn try_parse_x25519_public_key_raw_bytes(key_bytes: &[u8]) -> Result<LcPtr<EVP_PKEY>, KeyRejected> {
let expected_pub_key_len = X25519.id.pub_key_len();
if key_bytes.len() != expected_pub_key_len {
return Err(KeyRejected::invalid_encoding());
}
LcPtr::<EVP_PKEY>::parse_raw_public_key(key_bytes, EVP_PKEY_X25519)
}
#[cfg(test)]
mod agreement_tests;
#[cfg(test)]
mod parsed_public_key_tests;