use core::convert::TryInto;
use ecdsa::hazmat::{SignPrimitive, VerifyPrimitive};
use elliptic_curve::sec1::ToEncodedPoint;
use rand_core::{CryptoRng, RngCore};
use crate::{Error, Result};
impl From<elliptic_curve::Error> for Error {
fn from(_: elliptic_curve::Error) -> Self {
Self
}
}
impl From<p256::ecdsa::Error> for Error {
fn from(_: p256::ecdsa::Error) -> Self {
Self
}
}
#[derive(Clone)]
pub struct SecretKey(p256::SecretKey);
#[derive(Clone, Debug)]
pub struct PublicKey(p256::PublicKey);
#[derive(Clone)]
pub struct Keypair {
pub public: PublicKey,
pub secret: SecretKey,
}
#[derive(Clone, Debug)]
pub struct Signature(p256::ecdsa::Signature);
pub struct SharedSecret(p256::ecdh::SharedSecret);
impl Keypair {
pub fn random(rng: impl CryptoRng + RngCore) -> Self {
let secret = SecretKey(p256::SecretKey::random(rng));
let public = secret.public_key();
Keypair { public, secret }
}
}
impl SecretKey {
pub fn random(rng: impl CryptoRng + RngCore) -> Self {
SecretKey(p256::SecretKey::random(rng))
}
pub fn from_bytes(bytes: impl AsRef<[u8]>) -> Result<Self> {
Ok(SecretKey(p256::SecretKey::from_bytes(bytes)?))
}
pub unsafe fn to_bytes(&self) -> [u8; 32] {
let mut big_endian = [0u8; 32];
big_endian.copy_from_slice(&self.0.to_bytes());
big_endian
}
pub fn public_key(&self) -> PublicKey {
PublicKey(self.0.public_key())
}
pub fn sign_prehashed(&self, prehashed_message: &[u8], rng: impl CryptoRng + RngCore) -> Signature {
let prehashed_message_as_scalar = p256::Scalar::from_bytes_reduced(prehashed_message.try_into().unwrap());
let mut rng = rng;
let static_scalar = p256::Scalar::from_bytes_reduced(&self.0.to_bytes());
loop {
let ephemeral_secret = p256::SecretKey::random(&mut rng);
let ephemeral_scalar = p256::Scalar::from_bytes_reduced(&ephemeral_secret.to_bytes());
let blinded_scalar = p256::BlindedScalar::new(ephemeral_scalar, &mut rng);
if let Ok(signature) = static_scalar.try_sign_prehashed(
&blinded_scalar,
&prehashed_message_as_scalar,
) {
return Signature(signature);
}
}
}
#[cfg(feature = "prehash")]
#[cfg_attr(docsrs, doc(cfg(feature = "prehash")))]
pub fn sign(&self, message: &[u8], _rng: impl CryptoRng + RngCore) -> Signature {
let signer: p256::ecdsa::SigningKey = self.0.clone().into();
use p256::ecdsa::signature::Signer;
let signature = signer.sign(message);
Signature(signature)
}
pub fn agree(&self, other: &PublicKey) -> SharedSecret {
SharedSecret(elliptic_curve::ecdh::diffie_hellman(
self.0.to_secret_scalar(),
other.0.as_affine(),
))
}
}
impl PublicKey {
pub fn from_untagged_bytes(bytes: &[u8]) -> Result<Self> {
if bytes.len() != 64 {
return Err(Error);
}
let mut sec1_bytes = [4u8; 65];
sec1_bytes[1..].copy_from_slice(bytes);
Self::from_sec1_bytes(&sec1_bytes)
}
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
Ok(PublicKey(p256::PublicKey::from_sec1_bytes(bytes)?))
}
pub fn to_untagged_bytes(&self) -> [u8; 64] {
self.0.to_encoded_point(false).as_ref()[1..].as_ref().try_into().unwrap()
}
pub fn to_compressed_sec1_bytes(&self) -> [u8; 33] {
self.0.to_encoded_point(true).as_ref().try_into().unwrap()
}
pub fn to_uncompressed_sec1_bytes(&self) -> [u8; 65] {
self.0.to_encoded_point(false).as_ref().try_into().unwrap()
}
pub fn x(&self) -> [u8; 32] {
self.0.to_encoded_point(false).as_ref()[1..33].try_into().unwrap()
}
pub fn y(&self) -> [u8; 32] {
self.0.to_encoded_point(false).as_ref()[33..].try_into().unwrap()
}
#[must_use = "The return value indicates if the message is authentic"]
pub fn verify_prehashed(&self, prehashed_message: &[u8], signature: &Signature) -> bool {
let prehashed_message_as_scalar = p256::Scalar::from_bytes_reduced(prehashed_message.try_into().unwrap());
self.0.as_affine().verify_prehashed(&prehashed_message_as_scalar, &signature.0).is_ok()
}
#[cfg(feature = "prehash")]
#[cfg_attr(docsrs, doc(cfg(feature = "prehash")))]
#[must_use = "The return value indicates if the message is authentic"]
pub fn verify(&self, message: &[u8], signature: &Signature) -> bool {
let verifier: p256::ecdsa::VerifyingKey = self.0.clone().into();
use p256::ecdsa::signature::Verifier;
verifier.verify(message, &signature.0).is_ok()
}
}
impl Signature {
pub fn r(&self) -> [u8; 32] {
self.0.r().as_ref().to_bytes().into()
}
pub fn s(&self) -> [u8; 32] {
self.0.s().as_ref().to_bytes().into()
}
pub fn from_untagged_bytes(bytes: &[u8]) -> Result<Self> {
Ok(Signature(bytes.try_into()?))
}
pub fn from_sec1_bytes(bytes: &[u8]) -> Result<Self> {
Ok(Signature(p256::ecdsa::Signature::from_der(bytes)?))
}
pub fn to_untagged_bytes(&self) -> [u8; 64] {
self.0.as_ref().try_into().unwrap()
}
#[cfg(feature = "sec1-signatures")]
#[cfg_attr(docsrs, doc(cfg(feature = "sec1-signatures")))]
pub fn to_sec1_bytes(&self, buffer: &mut [u8; 72]) -> usize {
let asn1_signature = self.0.to_der();
let n = asn1_signature.as_ref().len();
buffer[..n].copy_from_slice(asn1_signature.as_ref());
n
}
}
impl SharedSecret {
pub fn as_bytes(&self) -> &[u8; 32] {
self.0.as_bytes().as_ref()
}
}