From a37e31ab0aaf7f323eab0c5a5c266c3d6bb6e80b Mon Sep 17 00:00:00 2001 From: Franziskus Kiefer Date: Mon, 30 Oct 2023 12:02:46 +0100 Subject: [PATCH] port some improvements from dev and make it usable in Bertie --- src/ecdh.rs | 319 ++++++++++++++++++++++++++++++++--------- src/hacl/curve25519.rs | 24 ++-- src/hacl/p256.rs | 38 +++-- src/hpke/kem.rs | 4 +- src/kem.rs | 200 +++++++++++++++++++++----- src/signature.rs | 12 +- 6 files changed, 462 insertions(+), 135 deletions(-) diff --git a/src/ecdh.rs b/src/ecdh.rs index d32a8322b..109229db3 100644 --- a/src/ecdh.rs +++ b/src/ecdh.rs @@ -46,22 +46,107 @@ pub enum Algorithm { } pub(crate) mod x25519 { + use rand::{CryptoRng, Rng}; + use super::Error; + pub struct PrivateKey(pub [u8; 32]); + pub struct PublicKey(pub [u8; 32]); + + impl From<&[u8; 32]> for PublicKey { + fn from(value: &[u8; 32]) -> Self { + Self(value.clone()) + } + } + + impl TryFrom<&[u8]> for PublicKey { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + Ok(Self(value.try_into().map_err(|_| Error::InvalidPoint)?)) + } + } + + impl TryFrom for PublicKey { + type Error = Error; + + fn try_from(value: crate::kem::PublicKey) -> Result { + if let crate::kem::PublicKey::X25519(k) = value { + Ok(k) + } else { + Err(Error::InvalidPoint) + } + } + } + + impl From<&[u8; 32]> for PrivateKey { + fn from(value: &[u8; 32]) -> Self { + Self(value.clone()) + } + } + + impl TryFrom<&[u8]> for PrivateKey { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + Ok(Self(value.try_into().map_err(|_| Error::InvalidScalar)?)) + } + } + + impl TryFrom for PrivateKey { + type Error = Error; + + fn try_from(value: crate::kem::PrivateKey) -> Result { + if let crate::kem::PrivateKey::X25519(k) = value { + Ok(k) + } else { + Err(Error::InvalidScalar) + } + } + } + + impl AsRef<[u8]> for PrivateKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsRef<[u8]> for PublicKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsRef<[u8; 32]> for PrivateKey { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } + } + + impl AsRef<[u8; 32]> for PublicKey { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } + } + #[cfg(all(bmi2, adx, target_arch = "x86_64"))] - pub(super) fn derive(p: &[u8; 32], s: &[u8; 32]) -> Result<[u8; 32], Error> { + pub(crate) fn derive(p: &PublicKey, s: &PrivateKey) -> Result { use crate::hacl::curve25519; use libcrux_platform::x25519_support; // On x64 we use vale if available or hacl as fallback. // Jasmin exists but is not verified yet. if x25519_support() { - curve25519::vale::ecdh(s, p).map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + curve25519::vale::ecdh(s, p) + .map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + .map(|p| PublicKey(p)) // XXX: not verified yet // crate::jasmin::x25519::mulx::derive(s, p) // .map_err(|e| Error::Custom(format!("Libjade Error {:?}", e))) } else { - curve25519::ecdh(s, p).map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + curve25519::ecdh(s, p) + .map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + .map(|p| PublicKey(p)) // XXX: not verified yet // crate::jasmin::x25519::derive(s, p) // .map_err(|e| Error::Custom(format!("Libjade Error {:?}", e))) @@ -72,66 +157,165 @@ pub(crate) mod x25519 { all(target_arch = "x86_64", any(not(bmi2), not(adx))), target_arch = "x86" ))] - pub(super) fn derive(p: &[u8; 32], s: &[u8; 32]) -> Result<[u8; 32], Error> { + pub(crate) fn derive(p: &PublicKey, s: &PrivateKey) -> Result { use crate::hacl::curve25519; // On x64 we use vale if available or hacl as fallback. // Jasmin exists but is not verified yet. - curve25519::ecdh(s, p).map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + curve25519::ecdh(s, p) + .map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + .map(|p| PublicKey(p)) // XXX: not verified yet // crate::jasmin::x25519::derive(s, p) // .map_err(|e| Error::Custom(format!("Libjade Error {:?}", e))) } #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - pub(super) fn derive(p: &[u8; 32], s: &[u8; 32]) -> Result<[u8; 32], Error> { + pub(crate) fn derive(p: &PublicKey, s: &PrivateKey) -> Result { // On any other platform we use the portable HACL implementation. use crate::hacl::curve25519; - curve25519::ecdh(s, p).map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + curve25519::ecdh(s, p) + .map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + .map(|p| PublicKey(p)) } // XXX: libjade's secret to public is broken on Windows (overflows the stack). // #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] - // pub(super) fn secret_to_public(s: &[u8; 32]) -> Result<[u8; 32], Error> { + // pub(super) fn secret_to_public(p: &PrivateKey) -> Result<[u8; 32], Error> { // crate::jasmin::x25519::secret_to_public(s).map_err(|e| Error::Custom(format!("Libjade Error {:?}", e))) // } // #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] - pub(super) fn secret_to_public(s: &[u8; 32]) -> Result<[u8; 32], Error> { + pub(crate) fn secret_to_public(s: &PrivateKey) -> Result { // On any other platform we use the portable HACL implementation. use crate::hacl::curve25519; - Ok(curve25519::secret_to_public(s)) + Ok(PublicKey(curve25519::secret_to_public(s))) + } + + /// Generate a new x25519 secret. + pub fn generate_secret(rng: &mut (impl CryptoRng + Rng)) -> Result { + const LIMIT: usize = 100; + for _ in 0..LIMIT { + let mut out = [0u8; 32]; + rng.try_fill_bytes(&mut out) + .map_err(|_| Error::KeyGenError)?; + + // We don't want a 0 key. + if out.iter().all(|&b| b == 0) { + continue; + } + + // We clamp the key already to make sure it can't be misused. + out[0] = out[0] & 248u8; + out[31] = out[31] & 127u8; + out[31] = out[31] | 64u8; + + return Ok(PrivateKey(out)); + } + + Err(Error::KeyGenError) + } + + /// Generate a new P256 key pair + pub fn key_gen(rng: &mut (impl CryptoRng + Rng)) -> Result<(PrivateKey, PublicKey), Error> { + let sk = generate_secret(rng)?; + let pk = secret_to_public(&sk)?; + Ok((sk, pk)) } } +pub use x25519::generate_secret as x25519_generate_secret; +pub use x25519::key_gen as x25519_key_gen; + pub(crate) mod p256 { + use rand::{CryptoRng, Rng}; + // P256 we only have in HACL use crate::hacl::p256; use super::Error; - pub(super) fn derive(p: &[u8; 64], s: &[u8; 32]) -> Result<[u8; 64], Error> { + pub struct PrivateKey(pub [u8; 32]); + pub struct PublicKey(pub [u8; 64]); + + impl From<&[u8; 64]> for PublicKey { + fn from(value: &[u8; 64]) -> Self { + Self(value.clone()) + } + } + + impl TryFrom<&[u8]> for PublicKey { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + Ok(Self(value.try_into().map_err(|_| Error::InvalidPoint)?)) + } + } + + impl From<&[u8; 32]> for PrivateKey { + fn from(value: &[u8; 32]) -> Self { + Self(value.clone()) + } + } + + impl TryFrom<&[u8]> for PrivateKey { + type Error = Error; + + fn try_from(value: &[u8]) -> Result { + Ok(Self(value.try_into().map_err(|_| Error::InvalidScalar)?)) + } + } + + impl AsRef<[u8]> for PrivateKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsRef<[u8]> for PublicKey { + fn as_ref(&self) -> &[u8] { + &self.0 + } + } + + impl AsRef<[u8; 32]> for PrivateKey { + fn as_ref(&self) -> &[u8; 32] { + &self.0 + } + } + + impl AsRef<[u8; 64]> for PublicKey { + fn as_ref(&self) -> &[u8; 64] { + &self.0 + } + } + + pub(super) fn derive(p: &PublicKey, s: &PrivateKey) -> Result { // We assume that the private key has been validated. - p256::ecdh(s, p).map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + p256::ecdh(s, p) + .map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + .map(|p| PublicKey(p)) } - pub(super) fn secret_to_public(s: &[u8; 32]) -> Result<[u8; 64], Error> { + pub(super) fn secret_to_public(s: &PrivateKey) -> Result { p256::validate_scalar(s).map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))?; - p256::secret_to_public(s).map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + p256::secret_to_public(s) + .map_err(|e| Error::Custom(format!("HACL Error {:?}", e))) + .map(|p| PublicKey(p)) } - pub fn validate_scalar(s: &[u8; 32]) -> Result<(), Error> { + pub fn validate_scalar(s: &PrivateKey) -> Result<(), Error> { p256::validate_scalar(s).map_err(|e| e.into()) } #[allow(unused)] - pub fn validate_point(p: &[u8; 64]) -> Result<(), Error> { + pub fn validate_point(p: &PublicKey) -> Result<(), Error> { p256::validate_point(p).map_err(|e| e.into()) } - pub(super) fn prepare_public_key(public_key: &[u8]) -> Result<[u8; 64], Error> { + pub(crate) fn prepare_public_key(public_key: &[u8]) -> Result { if public_key.is_empty() { return Err(Error::InvalidPoint); } @@ -148,13 +332,39 @@ pub(crate) mod p256 { public_key.try_into().map_err(|_| Error::InvalidPoint)? } }; + let pk = PublicKey(pk); p256::validate_point(&pk) .map(|()| pk) .map_err(|_| Error::InvalidPoint) } + + /// Generate a new p256 secret (scalar) + pub fn generate_secret(rng: &mut (impl CryptoRng + Rng)) -> Result { + const LIMIT: usize = 100; + for _ in 0..LIMIT { + let mut out = [0u8; 32]; + rng.try_fill_bytes(&mut out) + .map_err(|_| Error::KeyGenError)?; + + let out = PrivateKey(out); + if validate_scalar(&out).is_ok() { + return Ok(out); + } + } + Err(Error::KeyGenError) + } + + /// Generate a new P256 key pair + pub fn key_gen(rng: &mut (impl CryptoRng + Rng)) -> Result<(PrivateKey, PublicKey), Error> { + let sk = generate_secret(rng)?; + let pk = secret_to_public(&sk)?; + Ok((sk, pk)) + } } +pub use p256::generate_secret as p256_generate_secret; +pub use p256::key_gen as p256_key_gen; pub use p256::validate_scalar as p256_validate_scalar; /// Derive the ECDH shared secret. @@ -165,36 +375,38 @@ pub fn derive( scalar: impl AsRef<[u8]>, ) -> Result, Error> { match alg { - Algorithm::X25519 => x25519::derive( - point.as_ref().try_into().map_err(|_| Error::InvalidPoint)?, - scalar - .as_ref() - .try_into() - .map_err(|_| Error::InvalidScalar)?, - ) - .map(|r| r.into()), + Algorithm::X25519 => { + x25519::derive(&point.as_ref().try_into()?, &scalar.as_ref().try_into()?) + .map(|r| r.0.into()) + } Algorithm::P256 => { let point = p256::prepare_public_key(point.as_ref())?; let scalar = hacl::p256::validate_scalar_slice(scalar.as_ref()) .map_err(|_| Error::InvalidScalar)?; - p256::derive(&point, &scalar).map(|r| r.into()) + p256::derive(&point, &scalar).map(|r| r.0.into()) } _ => Err(Error::UnknownAlgorithm), } } +pub(crate) fn p256_derive( + point: &p256::PublicKey, + scalar: &p256::PrivateKey, +) -> Result { + p256::validate_point(point)?; + p256::validate_scalar(scalar)?; + + p256::derive(&point, &scalar) +} + /// Derive the public key for the provided secret key `scalar`. -pub fn secret_to_public(alg: Algorithm, scalar: &[u8]) -> Result, Error> { +pub fn secret_to_public(alg: Algorithm, scalar: impl AsRef<[u8]>) -> Result, Error> { match alg { Algorithm::X25519 => { - x25519::secret_to_public(scalar.try_into().map_err(|_| Error::InvalidScalar)?) - .map(|r| r.into()) - } - Algorithm::P256 => { - p256::secret_to_public(scalar.try_into().map_err(|_| Error::InvalidScalar)?) - .map(|r| r.into()) + x25519::secret_to_public(&scalar.as_ref().try_into()?).map(|r| r.0.into()) } + Algorithm::P256 => p256::secret_to_public(&scalar.as_ref().try_into()?).map(|r| r.0.into()), _ => Err(Error::UnknownAlgorithm), } } @@ -209,9 +421,7 @@ pub fn validate_scalar(alg: Algorithm, s: impl AsRef<[u8]>) -> Result<(), Error> Ok(()) } } - Algorithm::P256 => { - p256::validate_scalar(s.as_ref().try_into().map_err(|_| Error::InvalidScalar)?) - } + Algorithm::P256 => p256::validate_scalar(&s.as_ref().try_into()?), _ => Err(Error::UnknownAlgorithm), } } @@ -223,42 +433,9 @@ use rand::{CryptoRng, Rng}; /// The function returns the new scalar or an [`Error::KeyGenError`] if it was unable to /// generate a new key. If this happens, the provided `rng` is probably faulty. pub fn generate_secret(alg: Algorithm, rng: &mut (impl CryptoRng + Rng)) -> Result, Error> { - // We don't want to have an endless loop. 100 are more than enough - // iterations. If this doesn't work, the rng is broken. - const LIMIT: usize = 100; match alg { - Algorithm::X25519 => { - for _ in 0..LIMIT { - let mut out = [0u8; 32]; - rng.try_fill_bytes(&mut out) - .map_err(|_| Error::KeyGenError)?; - - // We don't want a 0 key. - if out.iter().all(|&b| b == 0) { - continue; - } - - // We clamp the key already to make sure it can't be misused. - out[0] = out[0] & 248u8; - out[31] = out[31] & 127u8; - out[31] = out[31] | 64u8; - - return Ok(out.into()); - } - return Err(Error::KeyGenError); - } - Algorithm::P256 => { - for _ in 0..LIMIT { - let mut out = [0u8; 32]; - rng.try_fill_bytes(&mut out) - .map_err(|_| Error::KeyGenError)?; - - if p256_validate_scalar(&out).is_ok() { - return Ok(out.into()); - } - } - return Err(Error::KeyGenError); - } + Algorithm::X25519 => x25519::generate_secret(rng).map(|k| k.0.to_vec()), + Algorithm::P256 => p256::generate_secret(rng).map(|k| k.0.to_vec()), _ => Err(Error::UnknownAlgorithm), } } diff --git a/src/hacl/curve25519.rs b/src/hacl/curve25519.rs index d31876ccf..820fa6303 100644 --- a/src/hacl/curve25519.rs +++ b/src/hacl/curve25519.rs @@ -11,13 +11,16 @@ pub enum Error { #[must_use] #[inline(always)] -pub fn ecdh(private_key: &[u8; 32], public_key: &[u8; 32]) -> Result<[u8; 32], Error> { +pub fn ecdh( + private_key: impl AsRef<[u8; 32]>, + public_key: impl AsRef<[u8; 32]>, +) -> Result<[u8; 32], Error> { let mut shared = [0u8; 32]; let ok = unsafe { Hacl_Curve25519_51_ecdh( shared.as_mut_ptr(), - private_key.as_ptr() as _, - public_key.as_ptr() as _, + private_key.as_ref().as_ptr() as _, + public_key.as_ref().as_ptr() as _, ) }; if !ok { @@ -34,9 +37,11 @@ pub fn ecdh(private_key: &[u8; 32], public_key: &[u8; 32]) -> Result<[u8; 32], E #[must_use] #[inline(always)] -pub fn secret_to_public(private_key: &[u8; 32]) -> [u8; 32] { +pub fn secret_to_public(private_key: impl AsRef<[u8; 32]>) -> [u8; 32] { let mut public = [0u8; 32]; - unsafe { Hacl_Curve25519_51_secret_to_public(public.as_mut_ptr(), private_key.as_ptr() as _) }; + unsafe { + Hacl_Curve25519_51_secret_to_public(public.as_mut_ptr(), private_key.as_ref().as_ptr() as _) + }; public } @@ -51,13 +56,16 @@ pub mod vale { #[must_use] #[inline(always)] - pub fn ecdh(private_key: &[u8; 32], public_key: &[u8; 32]) -> Result<[u8; 32], Error> { + pub fn ecdh( + private_key: impl AsRef<[u8; 32]>, + public_key: impl AsRef<[u8; 32]>, + ) -> Result<[u8; 32], Error> { let mut shared = [0u8; 32]; let ok = unsafe { Hacl_Curve25519_64_ecdh( shared.as_mut_ptr(), - private_key.as_ptr() as _, - public_key.as_ptr() as _, + private_key.as_ref().as_ptr() as _, + public_key.as_ref().as_ptr() as _, ) }; if !ok { diff --git a/src/hacl/p256.rs b/src/hacl/p256.rs index 0141915cf..22ebfd9ac 100644 --- a/src/hacl/p256.rs +++ b/src/hacl/p256.rs @@ -3,6 +3,8 @@ use libcrux_hacl::{ Hacl_P256_uncompressed_to_raw, Hacl_P256_validate_private_key, Hacl_P256_validate_public_key, }; +use crate::ecdh::p256::PrivateKey; + #[derive(Debug, PartialEq, Eq, Clone, Copy)] pub enum Error { InvalidInput, @@ -56,8 +58,8 @@ pub fn compressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> { /// /// Returns [`Error::InvalidPoint`] if the `point` is not valid. #[must_use] -pub fn validate_point(point: &[u8; 64]) -> Result<(), Error> { - if unsafe { Hacl_P256_validate_public_key(point.as_ptr() as _) } { +pub fn validate_point(point: impl AsRef<[u8; 64]>) -> Result<(), Error> { + if unsafe { Hacl_P256_validate_public_key(point.as_ref().as_ptr() as _) } { Ok(()) } else { Err(Error::InvalidPoint) @@ -67,13 +69,20 @@ pub fn validate_point(point: &[u8; 64]) -> Result<(), Error> { /// Validate a P256 secret key (scalar). /// /// Returns [`Error::InvalidScalar`] if the `scalar` is not valid. -pub fn validate_scalar(scalar: &[u8; 32]) -> Result<(), Error> { - if scalar.iter().all(|b| *b == 0) { +pub fn validate_scalar(scalar: &impl AsRef<[u8; 32]>) -> Result<(), Error> { + validate_scalar_(scalar.as_ref()) +} + +/// Validate a P256 secret key (scalar). +/// +/// Returns [`Error::InvalidScalar`] if the `scalar` is not valid. +pub fn validate_scalar_(scalar: &[u8; 32]) -> Result<(), Error> { + if scalar.as_ref().iter().all(|b| *b == 0) { return Err(Error::InvalidScalar); } // Ensure that the key is in range [1, p-1] - if unsafe { Hacl_P256_validate_private_key(scalar.as_ptr() as _) } { + if unsafe { Hacl_P256_validate_private_key(scalar.as_ref().as_ptr() as _) } { Ok(()) } else { Err(Error::InvalidScalar) @@ -81,7 +90,7 @@ pub fn validate_scalar(scalar: &[u8; 32]) -> Result<(), Error> { } /// Validate a P256 secret key (scalar). -pub fn validate_scalar_slice(scalar: &[u8]) -> Result<[u8; 32], Error> { +pub fn validate_scalar_slice(scalar: &[u8]) -> Result { if scalar.is_empty() { return Err(Error::InvalidScalar); } @@ -93,20 +102,23 @@ pub fn validate_scalar_slice(scalar: &[u8]) -> Result<[u8; 32], Error> { private[31 - i] = scalar[scalar.len() - 1 - i]; } - validate_scalar(&private).map(|_| private) + validate_scalar_(&private).map(|_| PrivateKey(private)) } /// Compute the ECDH with the `private_key` and `public_key`. /// /// Returns the 64 bytes shared key. #[must_use] -pub fn ecdh(private_key: &[u8; 32], public_key: &[u8; 64]) -> Result<[u8; 64], Error> { +pub fn ecdh( + private_key: impl AsRef<[u8; 32]>, + public_key: impl AsRef<[u8; 64]>, +) -> Result<[u8; 64], Error> { let mut shared = [0u8; 64]; let ok = unsafe { Hacl_P256_dh_responder( shared.as_mut_ptr(), - public_key.as_ptr() as _, - private_key.as_ptr() as _, + public_key.as_ref().as_ptr() as _, + private_key.as_ref().as_ptr() as _, ) }; if !ok { @@ -120,11 +132,11 @@ pub fn ecdh(private_key: &[u8; 32], public_key: &[u8; 64]) -> Result<[u8; 64], E /// /// Returns the 64 bytes public key. #[must_use] -pub fn secret_to_public(s: &[u8; 32]) -> Result<[u8; 64], Error> { - validate_scalar(s)?; +pub fn secret_to_public(s: impl AsRef<[u8; 32]>) -> Result<[u8; 64], Error> { + validate_scalar(&s)?; let mut out = [0u8; 64]; - if unsafe { Hacl_P256_dh_initiator(out.as_mut_ptr(), s.as_ptr() as _) } { + if unsafe { Hacl_P256_dh_initiator(out.as_mut_ptr(), s.as_ref().as_ptr() as _) } { Ok(out) } else { Err(Error::InvalidScalar) diff --git a/src/hpke/kem.rs b/src/hpke/kem.rs index da8a33552..a8015477b 100644 --- a/src/hpke/kem.rs +++ b/src/hpke/kem.rs @@ -243,7 +243,7 @@ fn shared_secret_from_dh(alg: KEM, mut secret: Vec) -> Vec { /// [`ValidationError`](`HpkeError::ValidationError`) as described in /// [validation](#validation-of-inputs-and-outputs). pub fn DH(alg: KEM, sk: &PrivateKeyIn, pk: &PublicKeyIn) -> Result { - match crate::ecdh::derive(kem_to_named_group(alg).into(), pk, sk) { + match crate::ecdh::derive(kem_to_named_group(alg).try_into().unwrap(), pk, sk) { Ok(secret) => HpkeBytesResult::Ok(shared_secret_from_dh(alg, secret)), Err(_) => HpkeBytesResult::Err(HpkeError::ValidationError), } @@ -444,7 +444,7 @@ pub fn DeriveKeyPair(alg: KEM, ikm: &InputKeyMaterial) -> Result for ecdh::Algorithm { - fn from(value: Algorithm) -> Self { +impl TryFrom for ecdh::Algorithm { + type Error = &'static str; + + fn try_from(value: Algorithm) -> Result { match value { - Algorithm::X25519 => ecdh::Algorithm::X25519, - Algorithm::X448 => ecdh::Algorithm::X448, - Algorithm::Secp256r1 => ecdh::Algorithm::P256, - Algorithm::Secp384r1 => ecdh::Algorithm::P384, - Algorithm::Secp521r1 => ecdh::Algorithm::P521, + Algorithm::X25519 => Ok(ecdh::Algorithm::X25519), + Algorithm::X448 => Ok(ecdh::Algorithm::X448), + Algorithm::Secp256r1 => Ok(ecdh::Algorithm::P256), + Algorithm::Secp384r1 => Ok(ecdh::Algorithm::P384), + Algorithm::Secp521r1 => Ok(ecdh::Algorithm::P521), + Algorithm::Kyber768X25519 => Ok(ecdh::Algorithm::X25519), + _ => Err("provided algorithm is not an ECDH algorithm"), } } } @@ -47,21 +61,119 @@ impl From for Error { /// A KEM private key. pub enum PrivateKey { - X25519([u8; 32]), - P256([u8; 32]), + X25519(x25519::PrivateKey), + P256(p256::PrivateKey), } /// A KEM public key. pub enum PublicKey { - X25519([u8; 32]), - P256([u8; 64]), + X25519(x25519::PublicKey), + P256(p256::PublicKey), +} + +/// A KEM ciphertext +pub enum Ct { + X25519(x25519::PublicKey), + P256(p256::PublicKey), +} + +/// A KEM shared secret +pub enum Ss { + X25519(x25519::PublicKey), + P256(p256::PublicKey), +} + +impl PrivateKey { + /// Encode a private key. + pub fn encode(&self) -> Vec { + match self { + PrivateKey::X25519(k) => k.0.to_vec(), + PrivateKey::P256(k) => k.0.to_vec(), + } + } + + /// Decode a private key. + pub fn decode(alg: Algorithm, bytes: &[u8]) -> Result { + match alg { + Algorithm::X25519 => bytes + .try_into() + .map_err(|_| Error::InvalidPrivateKey) + .map(|k| Self::X25519(k)), + Algorithm::Secp256r1 => bytes + .try_into() + .map_err(|_| Error::InvalidPrivateKey) + .map(|k| Self::P256(k)), + _ => Err(Error::UnsupportedAlgorithm), + } + } +} + +impl PublicKey { + /// Encode public key. + pub fn encode(&self) -> Vec { + match self { + PublicKey::X25519(k) => k.0.to_vec(), + PublicKey::P256(k) => k.0.to_vec(), + } + } + + /// Decode a public key. + pub fn decode(alg: Algorithm, bytes: &[u8]) -> Result { + match alg { + Algorithm::X25519 => bytes + .try_into() + .map_err(|_| Error::InvalidPublicKey) + .map(|k| Self::X25519(k)), + Algorithm::Secp256r1 => bytes + .try_into() + .map_err(|_| Error::InvalidPublicKey) + .map(|k| Self::P256(k)), + _ => Err(Error::UnsupportedAlgorithm), + } + } +} + +impl Ss { + /// Encode a shared secret. + pub fn encode(&self) -> Vec { + match self { + Ss::X25519(k) => k.0.to_vec(), + Ss::P256(k) => k.0.to_vec(), + } + } +} + +impl Ct { + /// Encode a ciphertext. + pub fn encode(&self) -> Vec { + match self { + Ct::X25519(k) => k.0.to_vec(), + Ct::P256(k) => k.0.to_vec(), + } + } + + /// Decode a ciphertext. + pub fn decode(alg: Algorithm, bytes: &[u8]) -> Result { + match alg { + Algorithm::X25519 => bytes + .try_into() + .map_err(|_| Error::InvalidCiphertext) + .map(|ct| Self::X25519(ct)), + Algorithm::Secp256r1 => bytes + .try_into() + .map_err(|_| Error::InvalidCiphertext) + .map(|ct| Self::P256(ct)), + _ => Err(Error::UnsupportedAlgorithm), + } + } } /// Compute the public key for a private key of the given [`Algorithm`]. +/// Applicable only to X25519 and secp256r1. pub fn secret_to_public(alg: Algorithm, sk: impl AsRef<[u8]>) -> Result, Error> { match alg { Algorithm::X25519 | Algorithm::Secp256r1 => { - ecdh::secret_to_public(alg.into(), sk.as_ref()).map_err(|e| e.into()) + ecdh::secret_to_public(alg.try_into().unwrap(), sk.as_ref()).map_err(|e| e.into()) } _ => Err(Error::UnsupportedAlgorithm), } @@ -75,38 +187,56 @@ pub fn secret_to_public(alg: Algorithm, sk: impl AsRef<[u8]>) -> Result, pub fn key_gen( alg: Algorithm, rng: &mut (impl CryptoRng + Rng), -) -> Result<(Vec, Vec), Error> { +) -> Result<(PrivateKey, PublicKey), Error> { match alg { - Algorithm::X25519 | Algorithm::Secp256r1 => { - ecdh::key_gen(alg.into(), rng).map_err(|e| e.into()) - } + Algorithm::X25519 => ecdh::x25519_key_gen(rng) + .map_err(|e| e.into()) + .map(|(private, public)| (PrivateKey::X25519(private), PublicKey::X25519(public))), + Algorithm::Secp256r1 => ecdh::p256_key_gen(rng) + .map_err(|e| e.into()) + .map(|(private, public)| (PrivateKey::P256(private), PublicKey::P256(public))), _ => Err(Error::UnsupportedAlgorithm), } } /// Encapsulate a shared secret to the provided `pk` and return the `(Key, Enc)` tuple. -pub fn encapsulate( - alg: Algorithm, - pk: &[u8], - rng: &mut (impl CryptoRng + Rng), -) -> Result<(Vec, Vec), Error> { - let (new_sk, new_pk) = key_gen(alg, rng)?; - match alg { - Algorithm::X25519 | Algorithm::Secp256r1 => { - let gxy = ecdh::derive(alg.into(), pk, &new_sk)?; - Ok((gxy, new_pk)) +pub fn encapsulate(pk: &PublicKey, rng: &mut (impl CryptoRng + Rng)) -> Result<(Ss, Ct), Error> { + match pk { + PublicKey::X25519(pk) => { + let (new_sk, new_pk) = ecdh::x25519_key_gen(rng)?; + let gxy = x25519::derive(pk, &new_sk)?; + Ok((Ss::X25519(gxy), Ct::X25519(new_pk))) + } + PublicKey::P256(pk) => { + let (new_sk, new_pk) = ecdh::p256_key_gen(rng)?; + let gxy = p256_derive(pk, &new_sk)?; + Ok((Ss::P256(gxy), Ct::P256(new_pk))) } - _ => Err(Error::UnsupportedAlgorithm), } } /// Decapsulate the shared secret in `ct` using the private key `sk`. -pub fn decapsulate(alg: Algorithm, ct: &[u8], sk: &[u8]) -> Result, Error> { - match alg { - Algorithm::X25519 | Algorithm::Secp256r1 => { - let gxy = ecdh::derive(alg.into(), ct, sk)?; - Ok(gxy) +pub fn decapsulate(ct: &Ct, sk: &PrivateKey) -> Result { + match ct { + Ct::X25519(ct) => { + let sk = if let PrivateKey::X25519(k) = sk { + k + } else { + return Err(Error::InvalidPrivateKey); + }; + x25519::derive(ct, sk) + .map_err(|e| e.into()) + .map(|k| Ss::X25519(k)) + } + Ct::P256(ct) => { + let sk = if let PrivateKey::P256(k) = sk { + k + } else { + return Err(Error::InvalidPrivateKey); + }; + p256_derive(ct, sk) + .map_err(|e| e.into()) + .map(|k| Ss::P256(k)) } - _ => Err(Error::UnsupportedAlgorithm), } } diff --git a/src/signature.rs b/src/signature.rs index afdb0aaa7..66260ca47 100644 --- a/src/signature.rs +++ b/src/signature.rs @@ -327,7 +327,7 @@ impl EcDsaP256Signature { fn ecdsa_p256_sign_prep( private_key: &[u8], rng: &mut (impl CryptoRng + RngCore), -) -> Result<([u8; 32], [u8; 32]), Error> { +) -> Result<(ecdh::p256::PrivateKey, [u8; 32]), Error> { let private_key = p256::validate_scalar_slice(private_key).map_err(|_| Error::SigningError)?; let mut nonce = [0u8; 32]; @@ -335,7 +335,7 @@ fn ecdsa_p256_sign_prep( rng.try_fill_bytes(&mut nonce) .map_err(|_| Error::SigningError)?; // Make sure it's a valid nonce. - if p256::validate_scalar(&nonce).is_ok() { + if p256::validate_scalar_slice(&nonce).is_ok() { break; } } @@ -373,7 +373,7 @@ pub fn sign( Algorithm::EcDsaP256(DigestAlgorithm::Sha256) => { let (private_key, nonce) = ecdsa_p256_sign_prep(private_key, rng)?; ecdsa_p256_sign_post( - p256::ecdsa::sign_sha256(payload, &private_key, &nonce) + p256::ecdsa::sign_sha256(payload, private_key.as_ref(), &nonce) .map_err(into_signing_error)?, alg, )? @@ -381,7 +381,7 @@ pub fn sign( Algorithm::EcDsaP256(DigestAlgorithm::Sha384) => { let (private_key, nonce) = ecdsa_p256_sign_prep(private_key, rng)?; ecdsa_p256_sign_post( - p256::ecdsa::sign_sha384(payload, &private_key, &nonce) + p256::ecdsa::sign_sha384(payload, private_key.as_ref(), &nonce) .map_err(into_signing_error)?, alg, )? @@ -389,7 +389,7 @@ pub fn sign( Algorithm::EcDsaP256(DigestAlgorithm::Sha512) => { let (private_key, nonce) = ecdsa_p256_sign_prep(private_key, rng)?; ecdsa_p256_sign_post( - p256::ecdsa::sign_sha512(payload, &private_key, &nonce) + p256::ecdsa::sign_sha512(payload, private_key.as_ref(), &nonce) .map_err(into_signing_error)?, alg, )? @@ -433,7 +433,7 @@ fn ecdsa_p256_verify_prep(public_key: &[u8]) -> Result<[u8; 64], Error> { } }; - p256::validate_point(&pk) + p256::validate_point(ecdh::p256::PublicKey(pk)) .map(|()| pk) .map_err(into_verify_error) }