use libcrux_p256::{
compressed_to_raw, ecdsa_sign_p256_sha2, ecdsa_sign_p256_sha384, ecdsa_sign_p256_sha512,
ecdsa_verif_p256_sha2, ecdsa_verif_p256_sha384, ecdsa_verif_p256_sha512, uncompressed_to_raw,
validate_private_key, validate_public_key,
};
use crate::DigestAlgorithm;
use super::Error;
#[derive(Clone, Default)]
pub struct Signature {
r: [u8; 32],
s: [u8; 32],
}
pub struct Nonce([u8; 32]);
pub struct PrivateKey([u8; 32]);
#[derive(Debug)]
pub struct PublicKey(pub [u8; 64]);
mod conversions {
use super::*;
impl Signature {
pub fn from_raw(r: [u8; 32], s: [u8; 32]) -> Self {
Self { r, s }
}
pub fn from_bytes(signature_bytes: [u8; 64]) -> Self {
Self {
r: signature_bytes[0..32].try_into().unwrap(),
s: signature_bytes[32..].try_into().unwrap(),
}
}
pub fn as_bytes(&self) -> (&[u8; 32], &[u8; 32]) {
(&self.r, &self.s)
}
}
impl TryFrom<&[u8; 32]> for PrivateKey {
type Error = Error;
fn try_from(value: &[u8; 32]) -> Result<Self, Self::Error> {
validate_private_key_slice(value)
}
}
impl TryFrom<&[u8]> for PrivateKey {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
validate_private_key_slice(value)
}
}
impl AsRef<[u8]> for PrivateKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8; 32]> for PrivateKey {
fn as_ref(&self) -> &[u8; 32] {
&self.0
}
}
impl TryFrom<&[u8; 64]> for PublicKey {
type Error = Error;
fn try_from(value: &[u8; 64]) -> Result<Self, Self::Error> {
validate_pk(value)
}
}
impl TryFrom<&[u8]> for PublicKey {
type Error = Error;
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
validate_pk(value)
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
&self.0
}
}
impl AsRef<[u8; 64]> for PublicKey {
fn as_ref(&self) -> &[u8; 64] {
&self.0
}
}
}
pub fn uncompressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
let mut concat_point = [0u8; 64];
if point.len() >= 65 {
if uncompressed_to_raw(point, &mut concat_point) {
Ok(concat_point)
} else {
Err(Error::InvalidInput)
}
} else {
Err(Error::NoCompressedPoint)
}
}
pub fn compressed_to_coordinates(point: &[u8]) -> Result<[u8; 64], Error> {
let mut concat_point = [0u8; 64];
if point.len() >= 33 {
if compressed_to_raw(point, &mut concat_point) {
Ok(concat_point)
} else {
Err(Error::InvalidInput)
}
} else {
Err(Error::NoUnCompressedPoint)
}
}
pub fn validate_point(point: &[u8]) -> Result<(), Error> {
if validate_public_key(point) {
Ok(())
} else {
Err(Error::InvalidPoint)
}
}
pub fn validate_scalar(scalar: &impl AsRef<[u8; 32]>) -> Result<(), Error> {
validate_scalar_(scalar.as_ref())
}
fn validate_scalar_(scalar: &[u8; 32]) -> Result<(), Error> {
if scalar.as_ref().iter().all(|b| *b == 0) {
return Err(Error::InvalidScalar);
}
if validate_private_key(scalar.as_ref()) {
Ok(())
} else {
Err(Error::InvalidScalar)
}
}
fn validate_scalar_slice(scalar: &[u8]) -> Result<[u8; 32], Error> {
if scalar.is_empty() {
return Err(Error::InvalidScalar);
}
let mut private = [0u8; 32];
let sk_len = if scalar.len() >= 32 { 32 } else { scalar.len() };
for i in 0..sk_len {
private[31 - i] = scalar[scalar.len() - 1 - i];
}
validate_scalar_(&private).map(|_| private)
}
fn validate_private_key_slice(scalar: &[u8]) -> Result<PrivateKey, Error> {
validate_scalar_slice(scalar).map(|a| PrivateKey(a))
}
#[cfg(feature = "rand")]
pub mod rand {
use crate::RAND_LIMIT;
use super::*;
use ::rand::{CryptoRng, RngCore, TryRngCore};
pub fn random_scalar(rng: &mut (impl CryptoRng + RngCore)) -> Result<[u8; 32], Error> {
let mut value = [0u8; 32];
for _ in 0..RAND_LIMIT {
rng.try_fill_bytes(&mut value)
.map_err(|_| Error::RandError)?;
if validate_scalar_slice(&value).is_ok() {
return Ok(value);
}
}
Err(Error::RandError)
}
impl Nonce {
pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Result<Self, Error> {
random_scalar(rng).map(|s| Self(s))
}
}
impl PrivateKey {
pub fn random(rng: &mut (impl CryptoRng + RngCore)) -> Result<Self, Error> {
random_scalar(rng).map(|s| Self(s))
}
}
pub fn sign(
hash: DigestAlgorithm,
payload: &[u8],
private_key: &PrivateKey,
rng: &mut (impl CryptoRng + RngCore),
) -> Result<Signature, Error> {
let nonce = Nonce(random_scalar(rng)?);
super::_sign(hash, payload, private_key, &nonce)
}
}
pub fn sign(
hash: DigestAlgorithm,
payload: &[u8],
private_key: &PrivateKey,
nonce: &Nonce,
) -> Result<Signature, Error> {
_sign(hash, payload, private_key, nonce)
}
fn _sign(
hash: DigestAlgorithm,
payload: &[u8],
private_key: &PrivateKey,
nonce: &Nonce,
) -> Result<Signature, Error> {
let mut signature = [0u8; 64];
let len = u32_len(payload)?;
let success = match hash {
DigestAlgorithm::Sha256 => {
ecdsa_sign_p256_sha2(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
}
DigestAlgorithm::Sha384 => {
ecdsa_sign_p256_sha384(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
}
DigestAlgorithm::Sha512 => {
ecdsa_sign_p256_sha512(&mut signature, len, payload, private_key.as_ref(), &nonce.0)
}
libcrux_sha2::Algorithm::Sha224 => return Err(Error::UnsupportedHash),
};
if !success {
return Err(Error::SigningError);
}
Ok(Signature {
r: signature[..32]
.try_into()
.map_err(|_| Error::SigningError)?,
s: signature[32..]
.try_into()
.map_err(|_| Error::SigningError)?,
})
}
fn u32_len(bytes: &[u8]) -> Result<u32, Error> {
if bytes.len() > u32::MAX as usize {
return Err(Error::InvalidInput);
} else {
Ok(bytes.len() as u32)
}
}
fn validate_pk(public_key: &[u8]) -> Result<PublicKey, Error> {
if public_key.is_empty() {
return Err(Error::SigningError);
}
let pk = if let Ok(pk) = uncompressed_to_coordinates(public_key) {
pk
} else {
if let Ok(pk) = compressed_to_coordinates(public_key) {
pk
} else {
public_key.try_into().map_err(|_| Error::InvalidSignature)?
}
};
let pk = PublicKey(pk);
validate_point(&pk.0).map(|_| pk)
}
pub fn verify(
hash: DigestAlgorithm,
payload: &[u8],
signature: &Signature,
public_key: &PublicKey,
) -> Result<(), Error> {
let len = u32_len(payload)?;
let success = match hash {
libcrux_sha2::Algorithm::Sha256 => {
ecdsa_verif_p256_sha2(len, payload, &public_key.0, &signature.r, &signature.s)
}
libcrux_sha2::Algorithm::Sha384 => {
ecdsa_verif_p256_sha384(len, payload, &public_key.0, &signature.r, &signature.s)
}
libcrux_sha2::Algorithm::Sha512 => {
ecdsa_verif_p256_sha512(len, payload, &public_key.0, &signature.r, &signature.s)
}
libcrux_sha2::Algorithm::Sha224 => return Err(Error::UnsupportedHash),
};
if success {
Ok(())
} else {
Err(Error::InvalidSignature)
}
}