#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use subtle::ConstantTimeEq;
use zeroize::{Zeroize, ZeroizeOnDrop};
use crate::classic::crypto_box::crypto_box_seed_keypair_inplace;
use crate::constants::{
CRYPTO_BOX_BEFORENMBYTES, CRYPTO_BOX_PUBLICKEYBYTES, CRYPTO_BOX_SECRETKEYBYTES,
CRYPTO_KX_SESSIONKEYBYTES,
};
use crate::error::Error;
use crate::kx;
use crate::precalc::PrecalcSecretKey;
use crate::types::*;
pub type PublicKey = StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>;
pub type SecretKey = StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>;
pub type StackKeyPair = KeyPair<PublicKey, SecretKey>;
#[cfg_attr(
feature = "serde",
derive(Zeroize, ZeroizeOnDrop, Serialize, Deserialize, Debug, Clone)
)]
#[cfg_attr(not(feature = "serde"), derive(Zeroize, ZeroizeOnDrop, Debug, Clone))]
pub struct KeyPair<
PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
> {
pub public_key: PublicKey,
pub secret_key: SecretKey,
}
impl<
PublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
SecretKey: NewByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
> KeyPair<PublicKey, SecretKey>
{
pub fn new() -> Self {
Self {
public_key: PublicKey::new_byte_array(),
secret_key: SecretKey::new_byte_array(),
}
}
pub fn gen() -> Self {
use crate::classic::crypto_box::crypto_box_keypair_inplace;
let mut public_key = PublicKey::new_byte_array();
let mut secret_key = SecretKey::new_byte_array();
crypto_box_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
Self {
public_key,
secret_key,
}
}
pub fn from_secret_key(secret_key: SecretKey) -> Self {
use crate::classic::crypto_core::crypto_scalarmult_base;
let mut public_key = PublicKey::new_byte_array();
crypto_scalarmult_base(public_key.as_mut_array(), secret_key.as_array());
Self {
public_key,
secret_key,
}
}
pub fn from_seed<Seed: Bytes>(seed: &Seed) -> Self {
let mut public_key = PublicKey::new_byte_array();
let mut secret_key = SecretKey::new_byte_array();
crypto_box_seed_keypair_inplace(
public_key.as_mut_array(),
secret_key.as_mut_array(),
seed.as_slice(),
);
Self {
public_key,
secret_key,
}
}
}
impl KeyPair<StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>, StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>> {
pub fn gen_with_defaults() -> Self {
Self::gen()
}
}
impl<
'a,
PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + std::convert::TryFrom<&'a [u8]> + Zeroize,
> KeyPair<PublicKey, SecretKey>
{
pub fn from_slices(public_key: &'a [u8], secret_key: &'a [u8]) -> Result<Self, Error> {
Ok(Self {
public_key: PublicKey::try_from(public_key)
.map_err(|_e| dryoc_error!("invalid public key"))?,
secret_key: SecretKey::try_from(secret_key)
.map_err(|_e| dryoc_error!("invalid secret key"))?,
})
}
}
impl<
PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
> KeyPair<PublicKey, SecretKey>
{
pub fn is_valid_public_key<PK: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(key: &PK) -> bool {
const ZERO_POINT: [u8; CRYPTO_BOX_PUBLICKEYBYTES] = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
let key_array = key.as_array();
if key_array == &ZERO_POINT {
return false;
}
if key_array[CRYPTO_BOX_PUBLICKEYBYTES - 1] & 0x80 != 0 {
return false;
}
true
}
pub fn is_valid_ed25519_key<PK: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(key: &PK) -> bool {
crate::classic::crypto_core::crypto_core_ed25519_is_valid_point_relaxed(key.as_array())
}
pub fn kx_new_client_session<SessionKey: NewByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize>(
&self,
server_public_key: &PublicKey,
) -> Result<kx::Session<SessionKey>, Error> {
kx::Session::new_client(self, server_public_key)
}
pub fn kx_new_server_session<SessionKey: NewByteArray<CRYPTO_KX_SESSIONKEYBYTES> + Zeroize>(
&self,
client_public_key: &PublicKey,
) -> Result<kx::Session<SessionKey>, Error> {
kx::Session::new_server(self, client_public_key)
}
#[inline]
pub fn precalculate(
&self,
third_party_public_key: &PublicKey,
) -> PrecalcSecretKey<StackByteArray<CRYPTO_BOX_BEFORENMBYTES>> {
PrecalcSecretKey::precalculate(third_party_public_key, &self.secret_key)
}
}
impl<
PublicKey: NewByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
SecretKey: NewByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
> Default for KeyPair<PublicKey, SecretKey>
{
fn default() -> Self {
Self::new()
}
}
#[cfg(any(feature = "nightly", all(doc, not(doctest))))]
#[cfg_attr(all(feature = "nightly", doc), doc(cfg(feature = "nightly")))]
pub mod protected {
use super::*;
use crate::classic::crypto_box::crypto_box_keypair_inplace;
pub use crate::protected::*;
impl
KeyPair<
Locked<HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>,
Locked<HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>>,
>
{
pub fn new_locked_keypair() -> Result<Self, std::io::Error> {
Ok(Self {
public_key: HeapByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::new_locked()?,
secret_key: HeapByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::new_locked()?,
})
}
pub fn gen_locked_keypair() -> Result<Self, std::io::Error> {
let mut res = Self::new_locked_keypair()?;
crypto_box_keypair_inplace(
res.public_key.as_mut_array(),
res.secret_key.as_mut_array(),
);
Ok(res)
}
#[inline]
pub fn precalculate_locked<OtherPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>(
&self,
third_party_public_key: &OtherPublicKey,
) -> Result<PrecalcSecretKey<Locked<HeapByteArray<CRYPTO_BOX_BEFORENMBYTES>>>, std::io::Error>
{
PrecalcSecretKey::precalculate_locked(third_party_public_key, &self.secret_key)
}
}
impl
KeyPair<
LockedRO<HeapByteArray<CRYPTO_BOX_PUBLICKEYBYTES>>,
LockedRO<HeapByteArray<CRYPTO_BOX_SECRETKEYBYTES>>,
>
{
pub fn gen_readonly_locked_keypair() -> Result<Self, std::io::Error> {
let mut public_key = HeapByteArray::<CRYPTO_BOX_PUBLICKEYBYTES>::new_locked()?;
let mut secret_key = HeapByteArray::<CRYPTO_BOX_SECRETKEYBYTES>::new_locked()?;
crypto_box_keypair_inplace(public_key.as_mut_array(), secret_key.as_mut_array());
let public_key = public_key.mprotect_readonly()?;
let secret_key = secret_key.mprotect_readonly()?;
Ok(Self {
public_key,
secret_key,
})
}
#[inline]
pub fn precalculate_readonly_locked<
OtherPublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
>(
&self,
third_party_public_key: &OtherPublicKey,
) -> Result<
PrecalcSecretKey<LockedRO<HeapByteArray<CRYPTO_BOX_BEFORENMBYTES>>>,
std::io::Error,
> {
PrecalcSecretKey::precalculate_readonly_locked(third_party_public_key, &self.secret_key)
}
}
}
impl<
PublicKey: ByteArray<CRYPTO_BOX_PUBLICKEYBYTES> + Zeroize,
SecretKey: ByteArray<CRYPTO_BOX_SECRETKEYBYTES> + Zeroize,
> PartialEq<KeyPair<PublicKey, SecretKey>> for KeyPair<PublicKey, SecretKey>
{
fn eq(&self, other: &Self) -> bool {
self.public_key
.as_slice()
.ct_eq(other.public_key.as_slice())
.unwrap_u8()
== 1
&& self
.secret_key
.as_slice()
.ct_eq(other.secret_key.as_slice())
.unwrap_u8()
== 1
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::kx::Session;
fn all_eq<T>(t: &[T], v: T) -> bool
where
T: PartialEq,
{
t.iter().all(|x| *x == v)
}
#[test]
fn test_new() {
let keypair = KeyPair::<
StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
>::new();
assert!(all_eq(&keypair.public_key, 0));
assert!(all_eq(&keypair.secret_key, 0));
}
#[test]
fn test_default() {
let keypair = KeyPair::<
StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
>::default();
assert!(all_eq(&keypair.public_key, 0));
assert!(all_eq(&keypair.secret_key, 0));
}
#[test]
fn test_gen_keypair() {
use sodiumoxide::crypto::scalarmult::curve25519::{Scalar, scalarmult_base};
use crate::classic::crypto_core::crypto_scalarmult_base;
let keypair = KeyPair::<
StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
>::gen();
let mut public_key = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
crypto_scalarmult_base(&mut public_key, keypair.secret_key.as_array());
assert_eq!(keypair.public_key.as_array(), &public_key);
let ge = scalarmult_base(&Scalar::from_slice(&keypair.secret_key).unwrap());
assert_eq!(ge.as_ref(), public_key);
}
#[test]
fn test_from_secret_key() {
let keypair_1 = KeyPair::<
StackByteArray<CRYPTO_BOX_PUBLICKEYBYTES>,
StackByteArray<CRYPTO_BOX_SECRETKEYBYTES>,
>::gen();
let keypair_2 = KeyPair::from_secret_key(keypair_1.secret_key.clone());
assert_eq!(keypair_1.public_key, keypair_2.public_key);
}
#[test]
fn test_keypair_precalculate() {
let kp1 = KeyPair::gen_with_defaults();
let kp2 = KeyPair::gen_with_defaults();
let precalc = kp1.precalculate(&kp2.public_key);
assert_eq!(precalc.len(), crate::constants::CRYPTO_BOX_BEFORENMBYTES);
}
#[cfg(feature = "nightly")]
#[test]
fn test_keypair_precalculate_locked() {
use crate::keypair::protected::*;
let kp1 = KeyPair::gen_locked_keypair().unwrap();
let kp2 = KeyPair::gen_locked_keypair().unwrap();
let precalc = kp1.precalculate_locked(&kp2.public_key).unwrap();
assert_eq!(precalc.len(), crate::constants::CRYPTO_BOX_BEFORENMBYTES);
}
#[test]
fn test_keypair_kx_new_client_session() {
let server_kp = KeyPair::gen_with_defaults();
let client_kp = KeyPair::gen_with_defaults();
let session: Session<StackByteArray<CRYPTO_KX_SESSIONKEYBYTES>> = client_kp
.kx_new_client_session(&server_kp.public_key)
.unwrap();
assert_eq!(
session.rx_as_slice().len(),
crate::constants::CRYPTO_KX_SESSIONKEYBYTES
);
assert_eq!(
session.tx_as_slice().len(),
crate::constants::CRYPTO_KX_SESSIONKEYBYTES
);
}
#[test]
fn test_keypair_kx_new_server_session() {
let client_kp = KeyPair::gen_with_defaults();
let server_kp = KeyPair::gen_with_defaults();
let session: Session<StackByteArray<CRYPTO_KX_SESSIONKEYBYTES>> = server_kp
.kx_new_server_session(&client_kp.public_key)
.unwrap();
assert_eq!(
session.rx_as_slice().len(),
crate::constants::CRYPTO_KX_SESSIONKEYBYTES
);
assert_eq!(
session.tx_as_slice().len(),
crate::constants::CRYPTO_KX_SESSIONKEYBYTES
);
}
#[test]
fn test_keypair_from_seed() {
let seed = [42u8; 32];
let kp: StackKeyPair = KeyPair::from_seed(&seed);
assert!(!kp.public_key.iter().all(|x| *x == 0));
}
#[test]
fn test_keypair_gen_with_defaults() {
let kp = KeyPair::gen_with_defaults();
assert!(!kp.public_key.iter().all(|x| *x == 0));
}
#[test]
fn test_is_valid_public_key() {
let valid_pk_bytes = [
215, 90, 152, 1, 130, 177, 10, 183, 213, 75, 254, 211, 201, 100, 7, 58, 14, 225, 114,
243, 218, 166, 35, 37, 175, 2, 26, 104, 247, 7, 81, 26,
];
let valid_pk = PublicKey::from(valid_pk_bytes);
assert!(
KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&valid_pk),
"Known valid key failed validation"
);
let mut invalid_high_bit_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
invalid_high_bit_bytes[31] = 0x80;
let invalid_high_bit = PublicKey::from(invalid_high_bit_bytes);
assert!(
!KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&invalid_high_bit),
"Key with high bit set should be invalid"
);
let zero_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
let zero_pk = PublicKey::from(zero_bytes);
assert!(
!KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&zero_pk),
"Zero key should be invalid"
);
let kp = KeyPair::gen_with_defaults();
assert!(
KeyPair::<PublicKey, SecretKey>::is_valid_public_key(&kp.public_key),
"Generated key failed validation"
);
}
#[test]
fn test_is_valid_ed25519_key() {
let (valid_pk, _) = crate::classic::crypto_sign::crypto_sign_keypair();
assert!(
KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&valid_pk),
"Ed25519 key from crypto_sign_keypair should pass relaxed validation"
);
let zero_bytes = [0u8; CRYPTO_BOX_PUBLICKEYBYTES];
let zero_pk = PublicKey::from(zero_bytes);
assert!(
!KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&zero_pk),
"Zero key should be invalid even with relaxed validation"
);
let identity_bytes = [
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0,
];
let identity_pk = PublicKey::from(identity_bytes);
assert!(
!KeyPair::<PublicKey, SecretKey>::is_valid_ed25519_key(&identity_pk),
"Identity element (small order point) should be invalid even with relaxed validation"
);
}
}