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::wolfcrypt_rs::{
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;
#[cfg(not(feature = "std"))]
use crate::prelude::*;
#[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,
}
}
#[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)
}
}
#[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))
}
}
pub const ECDH_P256: Algorithm = Algorithm {
id: AlgorithmID::ECDH_P256,
};
pub const ECDH_P384: Algorithm = Algorithm {
id: AlgorithmID::ECDH_P384,
};
pub const ECDH_P521: Algorithm = Algorithm {
id: AlgorithmID::ECDH_P521,
};
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()),
}
}
}
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,
}
}
}
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]
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))
}
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))
}
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(any(test, dev_tests_only))]
#[allow(missing_docs, clippy::missing_errors_doc)]
pub fn generate_for_test(
alg: &'static Algorithm,
rng: &mut dyn crate::rand::SecureRandom,
) -> Result<Self, Unspecified> {
match alg.id {
AlgorithmID::X25519 => {
let mut priv_key = [0u8; AlgorithmID::X25519.private_key_len()];
rng.mut_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.mut_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.mut_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.mut_fill(&mut priv_key)?;
Self::from_p521_private_key(&priv_key)
}
}
}
#[cfg(any(test, dev_tests_only))]
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(any(test, dev_tests_only))]
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(any(test, dev_tests_only))]
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(any(test, dev_tests_only))]
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),
})
}
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,
})
}
}
}
#[inline]
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.inner_key.algorithm()
}
}
impl AsDer<EcPrivateKeyRfc5915Der<'static>> for PrivateKey {
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_ptr())
})?
};
let length = usize::try_from(unsafe { i2d_ECPrivateKey(ec_key.as_const_ptr(), &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_ptr(), length)
}))
}
}
impl AsDer<Pkcs8V1Der<'static>> for PrivateKey {
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 {
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 {
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;
pub struct PublicKey {
inner_key: KeyInner,
key_bytes: [u8; MAX_PUBLIC_KEY_LEN],
len: usize,
}
impl PublicKey {
#[must_use]
pub fn algorithm(&self) -> &'static Algorithm {
self.inner_key.algorithm()
}
}
unsafe impl Send for PublicKey {}
unsafe impl Sync for PublicKey {}
impl Debug for PublicKey {
fn fmt(&self, f: &mut Formatter<'_>) -> core::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 {
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 {
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 {
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 {
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))
}
}
#[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> {
pub fn new(algorithm: &'static Algorithm, bytes: B) -> Self {
UnparsedPublicKey {
alg: algorithm,
bytes,
}
}
pub fn algorithm(&self) -> &'static Algorithm {
self.alg
}
pub fn bytes(&self) -> &B {
&self.bytes
}
}
#[derive(Debug, Clone)]
pub struct ParsedPublicKey {
format: ParsedPublicKeyFormat,
nid: i32,
key: LcPtr<EVP_PKEY>,
bytes: Box<[u8]>,
}
unsafe impl Send for ParsedPublicKey {}
unsafe impl Sync for ParsedPublicKey {}
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
#[non_exhaustive]
pub enum ParsedPublicKeyFormat {
X509,
Uncompressed,
Compressed,
Hybrid,
Raw,
Unknown,
}
impl ParsedPublicKey {
fn nid(&self) -> i32 {
self.nid
}
#[must_use]
pub fn format(&self) -> ParsedPublicKeyFormat {
self.format
}
pub(crate) fn mut_key(&mut self) -> &mut LcPtr<EVP_PKEY> {
&mut self.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 {
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()
}
}
#[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(mut 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.mut_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;