use crate::hacl;
#[derive(Debug, PartialEq, Eq)]
pub enum LowLevelError {
Jasmin(String),
Hacl(hacl::Error),
}
#[derive(Debug, PartialEq, Eq)]
pub enum Error {
InvalidPoint,
InvalidScalar,
UnknownAlgorithm,
KeyGenError,
Custom(String),
Wrap(LowLevelError),
}
impl From<hacl::p256::Error> for Error {
fn from(value: hacl::p256::Error) -> Self {
Error::Wrap(LowLevelError::Hacl(hacl::Error::P256(value)))
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub enum Algorithm {
X25519,
X448,
P256,
P384,
P521,
}
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<Self, Self::Error> {
Ok(Self(value.try_into().map_err(|_| Error::InvalidPoint)?))
}
}
impl TryFrom<crate::kem::PublicKey> for PublicKey {
type Error = Error;
fn try_from(value: crate::kem::PublicKey) -> Result<Self, Self::Error> {
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<Self, Self::Error> {
Ok(Self(value.try_into().map_err(|_| Error::InvalidScalar)?))
}
}
impl TryFrom<crate::kem::PrivateKey> for PrivateKey {
type Error = Error;
fn try_from(value: crate::kem::PrivateKey) -> Result<Self, Self::Error> {
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(crate) fn derive(p: &PublicKey, s: &PrivateKey) -> Result<PublicKey, Error> {
use crate::hacl::curve25519;
use libcrux_platform::x25519_support;
if x25519_support() {
curve25519::vale::ecdh(s, p)
.map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
.map(|p| PublicKey(p))
} else {
curve25519::ecdh(s, p)
.map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
.map(|p| PublicKey(p))
}
}
#[cfg(any(
all(target_arch = "x86_64", any(not(bmi2), not(adx))),
target_arch = "x86"
))]
pub(crate) fn derive(p: &PublicKey, s: &PrivateKey) -> Result<PublicKey, Error> {
use crate::hacl::curve25519;
curve25519::ecdh(s, p)
.map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
.map(|p| PublicKey(p))
}
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
pub(crate) fn derive(p: &PublicKey, s: &PrivateKey) -> Result<PublicKey, Error> {
use crate::hacl::curve25519;
curve25519::ecdh(s, p)
.map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
.map(|p| PublicKey(p))
}
pub(crate) fn secret_to_public(s: &PrivateKey) -> Result<PublicKey, Error> {
use crate::hacl::curve25519;
Ok(PublicKey(curve25519::secret_to_public(s)))
}
pub fn generate_secret(rng: &mut (impl CryptoRng + Rng)) -> Result<PrivateKey, Error> {
const LIMIT: usize = 100;
for _ in 0..LIMIT {
let mut out = [0u8; 32];
rng.try_fill_bytes(&mut out)
.map_err(|_| Error::KeyGenError)?;
if out.iter().all(|&b| b == 0) {
continue;
}
out[0] = out[0] & 248u8;
out[31] = out[31] & 127u8;
out[31] = out[31] | 64u8;
return Ok(PrivateKey(out));
}
Err(Error::KeyGenError)
}
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};
use crate::hacl::p256;
use super::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<Self, Self::Error> {
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<Self, Self::Error> {
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<PublicKey, Error> {
p256::ecdh(s, p)
.map_err(|e| Error::Custom(format!("HACL Error {:?}", e)))
.map(|p| PublicKey(p))
}
pub(super) fn secret_to_public(s: &PrivateKey) -> Result<PublicKey, Error> {
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)))
.map(|p| PublicKey(p))
}
pub fn validate_scalar(s: &PrivateKey) -> Result<(), Error> {
p256::validate_scalar(s).map_err(|e| e.into())
}
#[allow(unused)]
pub fn validate_point(p: &PublicKey) -> Result<(), Error> {
p256::validate_point(p).map_err(|e| e.into())
}
pub(crate) fn prepare_public_key(public_key: &[u8]) -> Result<PublicKey, Error> {
if public_key.is_empty() {
return Err(Error::InvalidPoint);
}
let pk = if let Ok(pk) = p256::uncompressed_to_coordinates(public_key) {
pk
} else {
if public_key.len() == 33 {
p256::compressed_to_coordinates(public_key).map_err(|_| Error::InvalidPoint)?
} else {
public_key.try_into().map_err(|_| Error::InvalidPoint)?
}
};
let pk = PublicKey(pk);
p256::validate_point(&pk)
.map(|()| pk)
.map_err(|_| Error::InvalidPoint)
}
pub fn generate_secret(rng: &mut (impl CryptoRng + Rng)) -> Result<PrivateKey, Error> {
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)
}
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;
pub fn derive(
alg: Algorithm,
point: impl AsRef<[u8]>,
scalar: impl AsRef<[u8]>,
) -> Result<Vec<u8>, Error> {
match alg {
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.0.into())
}
_ => Err(Error::UnknownAlgorithm),
}
}
pub(crate) fn p256_derive(
point: &p256::PublicKey,
scalar: &p256::PrivateKey,
) -> Result<p256::PublicKey, Error> {
p256::validate_point(point)?;
p256::validate_scalar(scalar)?;
p256::derive(&point, &scalar)
}
pub fn secret_to_public(alg: Algorithm, scalar: impl AsRef<[u8]>) -> Result<Vec<u8>, Error> {
match alg {
Algorithm::X25519 => {
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),
}
}
pub fn validate_scalar(alg: Algorithm, s: impl AsRef<[u8]>) -> Result<(), Error> {
match alg {
Algorithm::X25519 => {
if s.as_ref().iter().all(|&b| b == 0) {
Err(Error::InvalidScalar)
} else {
Ok(())
}
}
Algorithm::P256 => p256::validate_scalar(&s.as_ref().try_into()?),
_ => Err(Error::UnknownAlgorithm),
}
}
use rand::{CryptoRng, Rng};
pub fn generate_secret(alg: Algorithm, rng: &mut (impl CryptoRng + Rng)) -> Result<Vec<u8>, Error> {
match alg {
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),
}
}
pub fn key_gen(
alg: Algorithm,
rng: &mut (impl CryptoRng + Rng),
) -> Result<(Vec<u8>, Vec<u8>), Error> {
let sk = generate_secret(alg, rng)?;
let pk = secret_to_public(alg, &sk)?;
Ok((sk, pk))
}