use curve25519_dalek::edwards::CompressedEdwardsY;
use rand::RngExt;
use sha2::{Digest, Sha512};
use x25519_dalek::{PublicKey, StaticSecret};
use crate::crypto::types::{CryptoError, CryptoResult};
pub fn curve25519_key_pair() -> ([u8; 32], [u8; 32]) {
let random_bytes: [u8; 32] = rand::rng().random();
let secret = StaticSecret::from(random_bytes);
let public = PublicKey::from(&secret);
(public.to_bytes(), secret.to_bytes())
}
pub fn to_curve25519_pubkey(ed25519_pubkey: &[u8; 32]) -> CryptoResult<[u8; 32]> {
let compressed = CompressedEdwardsY(*ed25519_pubkey);
let point = compressed.decompress().ok_or_else(|| {
CryptoError::KeyConversionFailed(
"failed to decompress Ed25519 public key".to_string(),
)
})?;
Ok(point.to_montgomery().to_bytes())
}
pub fn to_curve25519_seckey(ed25519_seckey: &[u8; 64]) -> CryptoResult<[u8; 32]> {
let hash: [u8; 64] = Sha512::digest(&ed25519_seckey[..32]).into();
let mut scalar_bytes = [0u8; 32];
scalar_bytes.copy_from_slice(&hash[..32]);
scalar_bytes[0] &= 248;
scalar_bytes[31] &= 127;
scalar_bytes[31] |= 64;
Ok(scalar_bytes)
}
#[cfg(test)]
mod tests {
use super::*;
use hex_literal::hex;
#[test]
fn test_key_pair_random() {
let (pk1, sk1) = curve25519_key_pair();
let (pk2, sk2) = curve25519_key_pair();
assert_eq!(pk1.len(), 32);
assert_eq!(sk1.len(), 32);
assert_ne!(pk1, pk2);
assert_ne!(sk1, sk2);
}
#[test]
fn test_to_curve25519_pubkey() {
let ed_pk1 =
hex!("4cb76fdc6d32278e3f83dbf608360ecc6b65727934b85d2fb86862ff98c46ab7");
let ed_pk2 =
hex!("5ea34e72bb044654a6a23675690ef5ffaaf1656b02f93fb76655f9cbdbe89876");
let x_pk1 = to_curve25519_pubkey(&ed_pk1).unwrap();
let x_pk2 = to_curve25519_pubkey(&ed_pk2).unwrap();
assert_eq!(
hex::encode(x_pk1),
"d2ad010eeb72d72e561d9de7bd7b6989af77dcabffa03a5111a6c859ae5c3a72"
);
assert_eq!(
hex::encode(x_pk2),
"aa654f00fc39fc69fd0db829410ca38177d7732a8d2f0934ab3872ac56d5aa74"
);
}
#[test]
fn test_to_curve25519_seckey() {
let ed_sk1 = hex!(
"4cb76fdc6d32278e3f83dbf608360ecc6b65727934b85d2fb86862ff98c46ab7"
"8862834829a87e0afadfed763fa8785e893dbde7f2c001ff1071aa55005c347f"
);
let ed_sk2 = hex!(
"5ea34e72bb044654a6a23675690ef5ffaaf1656b02f93fb76655f9cbdbe89876"
"cd83ca3d13ad8a954d5011aa7861abe3a29ac25b70c4ed5234aff74d34ef5786"
);
let x_sk1 = to_curve25519_seckey(&ed_sk1).unwrap();
let x_sk2 = to_curve25519_seckey(&ed_sk2).unwrap();
assert_eq!(
hex::encode(x_sk1),
"207e5d97e761300f96c10adc11efdd6d5c15188a9a7682ec05b30ca017e9b447"
);
assert_eq!(
hex::encode(x_sk2),
"904943eff27142a8e5cd37c84e2437c9979a560b044bf9a65a8d644b325fe56a"
);
}
}