use crate::prelude_internal::*;
use digest::{FixedOutputReset, Output, OutputSizeUser};
use ed25519_dalek::{Signer, Verifier};
use elliptic_curve::bigint::Encoding;
#[cfg(feature = "openssl")]
use openssl::{
bn::{BigNum, BigNumContext},
ec::{self, EcGroupRef, EcKey},
pkey::{Private, Public},
};
use signature::DigestSigner;
#[derive(Clone, Copy, Debug)]
struct FakeDigest<'a>(&'a [u8]);
impl<'a> OutputSizeUser for FakeDigest<'a> {
type OutputSize = typenum::consts::U64;
}
impl<'a> Digest for FakeDigest<'a> {
fn new() -> Self {
unreachable!()
}
fn new_with_prefix(_data: impl AsRef<[u8]>) -> Self {
unreachable!()
}
fn update(&mut self, _data: impl AsRef<[u8]>) {
unreachable!()
}
fn chain_update(self, _data: impl AsRef<[u8]>) -> Self {
unreachable!()
}
fn finalize(self) -> Output<Self> {
*Output::<Self>::from_slice(self.0)
}
fn finalize_into(self, _out: &mut Output<Self>) {
unreachable!()
}
fn finalize_reset(&mut self) -> Output<Self> {
unreachable!()
}
fn finalize_into_reset(&mut self, _out: &mut Output<Self>)
where
Self: FixedOutputReset,
{
unreachable!()
}
fn reset(&mut self) {
unreachable!()
}
fn output_size() -> usize {
64
}
fn digest(_data: impl AsRef<[u8]>) -> Output<Self> {
unreachable!()
}
}
pub trait Ecc {
fn extract_ed25519_private(&self) -> Result<ed25519_dalek::SigningKey>;
fn extract_ed25519_public(&self) -> Result<ed25519_dalek::VerifyingKey> {
Ok((&self.extract_ed25519_private()?).into())
}
fn sign_ed25519(&self, msg: &[u8]) -> Result<[u8; 64]> {
Ok(self.extract_ed25519_private()?.sign(msg).to_bytes())
}
fn verify_ed25519(&self, msg: &[u8], sig: &[u8]) -> Result<()> {
self.extract_ed25519_public()?
.verify(msg, &ed25519_dalek::Signature::from_slice(sig)?)?;
Ok(())
}
fn sign_ed25519ph(&self, prehash: &[u8], context: Option<&[u8]>) -> Result<[u8; 64]> {
ensure!(
prehash.len() == 64,
"`prehash` must have a length of 64 bytes"
);
Ok(self
.extract_ed25519_private()?
.with_context(context.unwrap_or(&[]))?
.try_sign_digest(FakeDigest(prehash))?
.to_bytes())
}
fn verify_ed25519ph(&self, prehash: &[u8], context: Option<&[u8]>, sig: &[u8]) -> Result<()> {
ensure!(
prehash.len() == 64,
"`prehash` must have a length of 64 bytes"
);
self.extract_ed25519_public()?.verify_prehashed(
FakeDigest(prehash),
context,
&ed25519_dalek::Signature::from_slice(sig)?,
)?;
Ok(())
}
fn extract_x25519_private(&self) -> Result<[u8; 32]>;
fn extract_x25519_public(&self) -> Result<[u8; 32]> {
let static_secret = x25519_dalek::StaticSecret::from(self.extract_x25519_private()?);
let public_key = x25519_dalek::PublicKey::from(&static_secret);
Ok(public_key.to_bytes())
}
fn diffe_helman_x25519(&self, public: &[u8; 32]) -> Result<[u8; 32]> {
Ok(
x25519_dalek::StaticSecret::from(self.extract_x25519_private()?)
.diffie_hellman(&x25519_dalek::PublicKey::from(*public))
.to_bytes(),
)
}
fn extract_ec_v1_private_p256(&self) -> Result<p256::SecretKey>;
fn extract_ec_v1_public_p256(&self) -> Result<p256::PublicKey> {
Ok(self.extract_ec_v1_private_p256()?.public_key())
}
fn extract_ec_v1_private_secp256k1(&self) -> Result<k256::SecretKey>;
fn extract_ec_v1_public_secp256k1(&self) -> Result<k256::PublicKey> {
Ok(self.extract_ec_v1_private_secp256k1()?.public_key())
}
#[cfg(feature = "openssl")]
fn extract_ec_v1_private_openssl(&self, group: &EcGroupRef) -> Result<EcKey<Private>>;
#[cfg(feature = "openssl")]
fn extract_ec_v1_public_openssl(&self, group: &EcGroupRef) -> Result<EcKey<Public>> {
let private = self.extract_ec_v1_private_openssl(group)?;
Ok(ec::EcKey::from_public_key(group, private.public_key())?)
}
fn diffe_helman_ec_v1_p256(&self, public: &p256::PublicKey) -> Result<[u8; 32]> {
let private = self.extract_ec_v1_private_p256()?;
let output = p256::ecdh::diffie_hellman(private.to_nonzero_scalar(), public.as_affine());
let mut ret = [0u8; 32];
ret.copy_from_slice(output.raw_secret_bytes().as_slice());
Ok(ret)
}
}
impl Ecc for Secret {
fn extract_ed25519_private(&self) -> Result<ed25519_dalek::SigningKey> {
const SALT: &[u8] = b"\x00ED25519";
let mut key_bytes = [0u8; 32];
self.subsecret_from_salt(SALT)?
.extract_bytes_into(&mut key_bytes)?;
Ok(ed25519_dalek::SigningKey::from_bytes(&key_bytes))
}
fn extract_x25519_private(&self) -> Result<[u8; 32]> {
const SALT: &[u8] = b"\x00X25519";
let mut key_bytes = [0u8; 32];
self.subsecret_from_salt(SALT)?
.extract_bytes_into(&mut key_bytes[..])?;
Ok(key_bytes)
}
fn extract_ec_v1_private_p256(&self) -> Result<p256::SecretKey> {
use elliptic_curve::Curve;
const SALT: &[u8] = b"\x00EC_v1";
let order = p256::NistP256::ORDER.to_be_bytes();
let key_bytes = self
.subsecret_from_salt(SALT)?
.extract_int_to_be_vec(&order)?;
Ok(p256::SecretKey::from_slice(&key_bytes)?)
}
fn extract_ec_v1_private_secp256k1(&self) -> Result<k256::SecretKey> {
use elliptic_curve::Curve;
const SALT: &[u8] = b"\x00EC_v1";
let order = k256::Secp256k1::ORDER.to_be_bytes();
let key_bytes = self
.subsecret_from_salt(SALT)?
.extract_int_to_be_vec(&order)?;
Ok(k256::SecretKey::from_slice(&key_bytes)?)
}
#[cfg(feature = "openssl")]
fn extract_ec_v1_private_openssl(&self, group: &EcGroupRef) -> Result<EcKey<Private>> {
const SALT: &[u8] = b"\x00EC_v1";
let mut ctx = BigNumContext::new_secure()?;
let mut order = BigNum::new()?;
group.order(&mut order, &mut ctx)?;
let private_key = self.subsecret_from_salt(SALT)?.extract_big_num(&order)?;
let mut public_key = ec::EcPoint::new(group)?;
public_key.mul_generator(group, &private_key, &ctx)?;
let key = ec::EcKey::from_private_components(group, &private_key, &public_key)?;
key.check_key()?;
Ok(key)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[cfg(feature = "openssl")]
use openssl::nid::Nid;
use sha2::Sha512;
#[test]
fn test_ed25519() {
let secret = Secret::ZERO;
let msg = b"Hello, World!";
let sig = secret.sign_ed25519(msg).unwrap();
secret.verify_ed25519(msg, &sig).unwrap();
assert!(secret.verify_ed25519(b"Goodbye!", &sig).is_err());
}
#[test]
fn test_ed25519ph() {
let secret = Secret::ZERO;
let msg = b"Hello, World!";
let prehash = Sha512::digest(msg);
let sig = secret.sign_ed25519ph(prehash.as_slice(), None).unwrap();
secret
.verify_ed25519ph(prehash.as_slice(), None, &sig)
.unwrap();
}
#[test]
fn test_ed25519ph_context() {
let secret = Secret::ZERO;
let msg = b"Hello, World!";
let prehash = Sha512::digest(msg);
let context = Some(b"MyContext".as_slice());
let sig = secret.sign_ed25519ph(prehash.as_slice(), context).unwrap();
secret
.verify_ed25519ph(prehash.as_slice(), context, &sig)
.unwrap();
}
#[test]
#[allow(non_snake_case)]
fn test_ecdh_x25519() {
let secretA = Secret::ZERO.subsecret_from_label("secretA").unwrap();
let secretB = Secret::ZERO.subsecret_from_label("secretB").unwrap();
let sessionAB = secretA
.diffe_helman_x25519(&secretB.extract_x25519_public().unwrap())
.unwrap();
let sessionBA = secretB
.diffe_helman_x25519(&secretA.extract_x25519_public().unwrap())
.unwrap();
assert_eq!(sessionAB, sessionBA);
}
#[test]
#[allow(non_snake_case)]
fn test_ecdh_p256() {
let secretA = Secret::ZERO.subsecret_from_label("secretA").unwrap();
let secretB = Secret::ZERO.subsecret_from_label("secretB").unwrap();
let sessionAB = secretA
.diffe_helman_ec_v1_p256(&secretB.extract_ec_v1_public_p256().unwrap())
.unwrap();
let sessionBA = secretB
.diffe_helman_ec_v1_p256(&secretA.extract_ec_v1_public_p256().unwrap())
.unwrap();
assert_eq!(sessionAB, sessionBA);
}
#[cfg(feature = "openssl")]
#[test]
fn test_extract_ec_v1_private() {
let group = ec::EcGroup::from_curve_name(Nid::SECP256K1).unwrap();
let ec = Secret::ZERO.extract_ec_v1_private_openssl(&group).unwrap();
let pem = ec.private_key_to_pem().unwrap();
let pem_str = std::str::from_utf8(&pem).unwrap();
println!("{pem_str}");
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(pem_str.as_bytes());
assert_eq!(
hex::encode(hasher.finalize()),
"50916d8e3ac8229c94f1507d75b74d23be6ce791bcd1a3ee5d55c8ecc321d9f3".to_string()
);
}
#[cfg(feature = "openssl")]
#[test]
fn test_extract_ec_v1_public() {
let group = ec::EcGroup::from_curve_name(Nid::SECP256K1).unwrap();
let ec = Secret::ZERO.extract_ec_v1_public_openssl(&group).unwrap();
let pem = ec.public_key_to_pem().unwrap();
let pem_str = std::str::from_utf8(&pem).unwrap();
println!("{pem_str}");
use sha2::{Digest, Sha256};
let mut hasher = Sha256::new();
hasher.update(pem_str.as_bytes());
assert_eq!(
hex::encode(hasher.finalize()),
"fa66fb153fe8426a52e85122ea02fa5a06eb905f9fa350d8a448486383733696".to_string()
);
}
}