use core::convert::AsRef;
use core::fmt::{Debug};
use rand_core::{RngCore, CryptoRng};
use curve25519_dalek::constants;
use curve25519_dalek::ristretto::{CompressedRistretto, RistrettoPoint};
use curve25519_dalek::scalar::Scalar;
use subtle::{Choice, ConstantTimeEq};
use zeroize::Zeroize;
use crate::scalars;
use crate::points::RistrettoBoth;
use crate::errors::{SignatureError, SignatureResult};
pub const MINI_SECRET_KEY_LENGTH: usize = 32;
pub const PUBLIC_KEY_LENGTH: usize = 32;
const SECRET_KEY_KEY_LENGTH: usize = 32;
const SECRET_KEY_NONCE_LENGTH: usize = 32;
pub const SECRET_KEY_LENGTH: usize = SECRET_KEY_KEY_LENGTH + SECRET_KEY_NONCE_LENGTH;
pub const KEYPAIR_LENGTH: usize = SECRET_KEY_LENGTH + PUBLIC_KEY_LENGTH;
pub enum ExpansionMode {
Uniform,
Ed25519,
}
#[derive(Clone, Zeroize)]
#[zeroize(drop)]
pub struct MiniSecretKey(pub(crate) [u8; MINI_SECRET_KEY_LENGTH]);
impl Debug for MiniSecretKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "MiniSecretKey: {:?}", &self.0[..])
}
}
impl Eq for MiniSecretKey {}
impl PartialEq for MiniSecretKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).unwrap_u8() == 1u8
}
}
impl ConstantTimeEq for MiniSecretKey {
fn ct_eq(&self, other: &Self) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl MiniSecretKey {
const DESCRIPTION: &'static str = "Analogous to ed25519 secret key as 32 bytes, see RFC8032.";
pub const UNIFORM_MODE: ExpansionMode = ExpansionMode::Uniform;
pub const ED25519_MODE: ExpansionMode = ExpansionMode::Ed25519;
fn expand_uniform(&self) -> SecretKey {
let mut t = merlin::Transcript::new(b"ExpandSecretKeys");
t.append_message(b"mini", &self.0[..]);
let mut scalar_bytes = [0u8; 64];
t.challenge_bytes(b"sk", &mut scalar_bytes);
let key = Scalar::from_bytes_mod_order_wide(&scalar_bytes);
let mut nonce = [0u8; 32];
t.challenge_bytes(b"no", &mut nonce);
SecretKey { key, nonce }
}
fn expand_ed25519(&self) -> SecretKey {
use sha2::{
Sha512,
digest::{Update, FixedOutput},
};
let mut h = Sha512::default();
h.update(self.as_bytes());
let r = h.finalize_fixed();
let mut key = [0u8; 32];
key.copy_from_slice(&r.as_slice()[0..32]);
key[0] &= 248;
key[31] &= 63;
key[31] |= 64;
scalars::divide_scalar_bytes_by_cofactor(&mut key);
#[allow(deprecated)] let key = Scalar::from_bits(key);
let mut nonce = [0u8; 32];
nonce.copy_from_slice(&r.as_slice()[32..64]);
SecretKey { key, nonce }
}
pub fn expand(&self, mode: ExpansionMode) -> SecretKey {
match mode {
ExpansionMode::Uniform => self.expand_uniform(),
ExpansionMode::Ed25519 => self.expand_ed25519(),
}
}
pub fn expand_to_keypair(&self, mode: ExpansionMode) -> Keypair {
self.expand(mode).into()
}
pub fn expand_to_public(&self, mode: ExpansionMode) -> PublicKey {
self.expand(mode).to_public()
}
#[inline]
pub fn to_bytes(&self) -> [u8; MINI_SECRET_KEY_LENGTH] {
self.0
}
#[inline]
pub fn as_bytes(&self) -> &[u8; MINI_SECRET_KEY_LENGTH] {
&self.0
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> SignatureResult<MiniSecretKey> {
if bytes.len() != MINI_SECRET_KEY_LENGTH {
return Err(SignatureError::BytesLengthError {
name: "MiniSecretKey",
description: MiniSecretKey::DESCRIPTION,
length: MINI_SECRET_KEY_LENGTH,
});
}
let mut bits: [u8; 32] = [0u8; 32];
bits.copy_from_slice(&bytes[..32]);
Ok(MiniSecretKey(bits))
}
pub fn generate_with<R>(mut csprng: R) -> MiniSecretKey
where
R: CryptoRng + RngCore,
{
let mut sk: MiniSecretKey = MiniSecretKey([0u8; 32]);
csprng.fill_bytes(&mut sk.0);
sk
}
#[cfg(feature = "getrandom")]
pub fn generate() -> MiniSecretKey {
Self::generate_with(super::getrandom_or_panic())
}
}
serde_boilerplate!(MiniSecretKey);
#[derive(Clone, Zeroize)]
#[zeroize(drop)]
pub struct SecretKey {
pub(crate) key: Scalar,
pub(crate) nonce: [u8; 32],
}
impl Debug for SecretKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "SecretKey {{ key: {:?} nonce: {:?} }}", &self.key, &self.nonce)
}
}
impl Eq for SecretKey {}
impl PartialEq for SecretKey {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).unwrap_u8() == 1u8
}
}
impl ConstantTimeEq for SecretKey {
fn ct_eq(&self, other: &Self) -> Choice {
self.key.ct_eq(&other.key)
}
}
impl SecretKey {
const DESCRIPTION: &'static str =
"An ed25519-like expanded secret key as 64 bytes, as specified in RFC8032.";
#[inline]
pub fn to_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
let mut bytes: [u8; 64] = [0u8; 64];
bytes[..32].copy_from_slice(&self.key.to_bytes()[..]);
bytes[32..].copy_from_slice(&self.nonce[..]);
bytes
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> SignatureResult<SecretKey> {
if bytes.len() != SECRET_KEY_LENGTH {
return Err(SignatureError::BytesLengthError {
name: "SecretKey",
description: SecretKey::DESCRIPTION,
length: SECRET_KEY_LENGTH,
});
}
let mut key: [u8; 32] = [0u8; 32];
key.copy_from_slice(&bytes[00..32]);
let key =
crate::scalar_from_canonical_bytes(key).ok_or(SignatureError::ScalarFormatError)?;
let mut nonce: [u8; 32] = [0u8; 32];
nonce.copy_from_slice(&bytes[32..64]);
Ok(SecretKey { key, nonce })
}
#[inline]
pub fn to_ed25519_bytes(&self) -> [u8; SECRET_KEY_LENGTH] {
let mut bytes: [u8; 64] = [0u8; 64];
let mut key = self.key.to_bytes();
scalars::multiply_scalar_bytes_by_cofactor(&mut key);
bytes[..32].copy_from_slice(&key[..]);
bytes[32..].copy_from_slice(&self.nonce[..]);
bytes
}
#[inline]
pub fn from_ed25519_bytes(bytes: &[u8]) -> SignatureResult<SecretKey> {
if bytes.len() != SECRET_KEY_LENGTH {
return Err(SignatureError::BytesLengthError {
name: "SecretKey",
description: SecretKey::DESCRIPTION,
length: SECRET_KEY_LENGTH,
});
}
let mut key: [u8; 32] = [0u8; 32];
key.copy_from_slice(&bytes[00..32]);
scalars::divide_scalar_bytes_by_cofactor(&mut key);
let key = Scalar::from_canonical_bytes(key);
if bool::from(key.is_none()) {
return Err(SignatureError::InvalidKey);
}
let key = key.unwrap();
let mut nonce: [u8; 32] = [0u8; 32];
nonce.copy_from_slice(&bytes[32..64]);
Ok(SecretKey { key, nonce })
}
pub fn generate_with<R>(mut csprng: R) -> SecretKey
where
R: CryptoRng + RngCore,
{
let mut key: [u8; 64] = [0u8; 64];
csprng.fill_bytes(&mut key);
let mut nonce: [u8; 32] = [0u8; 32];
csprng.fill_bytes(&mut nonce);
SecretKey { key: Scalar::from_bytes_mod_order_wide(&key), nonce }
}
#[cfg(feature = "getrandom")]
pub fn generate() -> SecretKey {
Self::generate_with(super::getrandom_or_panic())
}
pub fn to_public(&self) -> PublicKey {
PublicKey::from_point(&self.key * constants::RISTRETTO_BASEPOINT_TABLE)
}
pub fn to_keypair(self) -> Keypair {
let public = self.to_public();
Keypair { secret: self, public }
}
}
serde_boilerplate!(SecretKey);
#[derive(Copy, Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PublicKey(pub(crate) RistrettoBoth);
impl Debug for PublicKey {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "PublicKey( {:?} )", self.0)
}
}
impl ConstantTimeEq for PublicKey {
fn ct_eq(&self, other: &PublicKey) -> Choice {
self.0.ct_eq(&other.0)
}
}
impl AsRef<[u8]> for PublicKey {
fn as_ref(&self) -> &[u8] {
self.as_compressed().as_bytes()
}
}
#[rustfmt::skip]
impl PublicKey {
const DESCRIPTION: &'static str = "A Ristretto Schnorr public key represented as a 32-byte Ristretto compressed point";
pub fn as_compressed(&self) -> &CompressedRistretto { self.0.as_compressed() }
pub fn into_compressed(self) -> CompressedRistretto { self.0.into_compressed() }
pub fn as_point(&self) -> &RistrettoPoint { self.0.as_point() }
pub fn into_point(self) -> RistrettoPoint { self.0.into_point() }
pub fn from_compressed(compressed: CompressedRistretto) -> SignatureResult<PublicKey> {
Ok(PublicKey(RistrettoBoth::from_compressed(compressed) ?))
}
pub fn from_point(point: RistrettoPoint) -> PublicKey {
PublicKey(RistrettoBoth::from_point(point))
}
#[inline]
pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
self.as_compressed().to_bytes()
}
#[inline]
pub fn from_bytes(bytes: &[u8]) -> SignatureResult<PublicKey> {
Ok(PublicKey(RistrettoBoth::from_bytes_ser("PublicKey",PublicKey::DESCRIPTION,bytes) ?))
}
}
impl From<SecretKey> for PublicKey {
fn from(source: SecretKey) -> PublicKey {
source.to_public()
}
}
serde_boilerplate!(PublicKey);
#[derive(Clone, Debug)]
pub struct Keypair {
pub secret: SecretKey,
pub public: PublicKey,
}
impl Zeroize for Keypair {
fn zeroize(&mut self) {
self.secret.zeroize();
}
}
impl Drop for Keypair {
fn drop(&mut self) {
self.zeroize();
}
}
impl From<SecretKey> for Keypair {
fn from(secret: SecretKey) -> Keypair {
let public = secret.to_public();
Keypair { secret, public }
}
}
impl Keypair {
const DESCRIPTION: &'static str = "A 96 bytes Ristretto Schnorr keypair";
pub fn to_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret.to_bytes());
bytes[SECRET_KEY_LENGTH..].copy_from_slice(&self.public.to_bytes());
bytes
}
pub fn from_bytes(bytes: &[u8]) -> SignatureResult<Keypair> {
if bytes.len() != KEYPAIR_LENGTH {
return Err(SignatureError::BytesLengthError {
name: "Keypair",
description: Keypair::DESCRIPTION,
length: KEYPAIR_LENGTH,
});
}
let secret = SecretKey::from_bytes(&bytes[..SECRET_KEY_LENGTH])?;
let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?;
Ok(Keypair { secret, public })
}
pub fn to_half_ed25519_bytes(&self) -> [u8; KEYPAIR_LENGTH] {
let mut bytes: [u8; KEYPAIR_LENGTH] = [0u8; KEYPAIR_LENGTH];
bytes[..SECRET_KEY_LENGTH].copy_from_slice(&self.secret.to_ed25519_bytes());
bytes[SECRET_KEY_LENGTH..].copy_from_slice(&self.public.to_bytes());
bytes
}
pub fn from_half_ed25519_bytes(bytes: &[u8]) -> SignatureResult<Keypair> {
if bytes.len() != KEYPAIR_LENGTH {
return Err(SignatureError::BytesLengthError {
name: "Keypair",
description: Keypair::DESCRIPTION,
length: KEYPAIR_LENGTH,
});
}
let secret = SecretKey::from_ed25519_bytes(&bytes[..SECRET_KEY_LENGTH])?;
let public = PublicKey::from_bytes(&bytes[SECRET_KEY_LENGTH..])?;
Ok(Keypair { secret, public })
}
pub fn generate_with<R>(csprng: R) -> Keypair
where
R: CryptoRng + RngCore,
{
let secret: SecretKey = SecretKey::generate_with(csprng);
let public: PublicKey = secret.to_public();
Keypair { public, secret }
}
#[cfg(feature = "getrandom")]
pub fn generate() -> Keypair {
Self::generate_with(super::getrandom_or_panic())
}
}
serde_boilerplate!(Keypair);
#[cfg(test)]
mod test {
use super::*;
#[test]
fn derives_from_core() {
let pk_d = PublicKey::default();
debug_assert_eq!(pk_d.as_point().compress(), CompressedRistretto::default());
debug_assert_eq!(pk_d.as_compressed().decompress().unwrap(), RistrettoPoint::default());
}
#[cfg(feature = "getrandom")]
#[test]
fn keypair_zeroize() {
let mut csprng = rand_core::OsRng;
let mut keypair = Keypair::generate_with(&mut csprng);
keypair.zeroize();
fn as_bytes<T>(x: &T) -> &[u8] {
use core::mem;
use core::slice;
unsafe { slice::from_raw_parts(x as *const T as *const u8, mem::size_of_val(x)) }
}
assert!(!as_bytes(&keypair).iter().all(|x| *x == 0u8));
}
#[cfg(feature = "getrandom")]
#[test]
fn pubkey_from_mini_secret_and_expanded_secret() {
let mut csprng = rand_core::OsRng;
let mini_secret: MiniSecretKey = MiniSecretKey::generate_with(&mut csprng);
let secret: SecretKey = mini_secret.expand(ExpansionMode::Ed25519);
let public_from_mini_secret: PublicKey =
mini_secret.expand_to_public(ExpansionMode::Ed25519);
let public_from_secret: PublicKey = secret.to_public();
assert!(public_from_mini_secret == public_from_secret);
let secret: SecretKey = mini_secret.expand(ExpansionMode::Uniform);
let public_from_mini_secret: PublicKey =
mini_secret.expand_to_public(ExpansionMode::Uniform);
let public_from_secret: PublicKey = secret.to_public();
assert!(public_from_mini_secret == public_from_secret);
}
#[cfg(feature = "getrandom")]
#[test]
fn secret_key_can_be_converted_to_ed25519_bytes_and_back() {
let count = if cfg!(debug_assertions) { 200000 } else { 2000000 };
for _ in 0..count {
let key = SecretKey::generate();
let bytes = key.to_ed25519_bytes();
let key_deserialized = SecretKey::from_ed25519_bytes(&bytes).unwrap();
assert_eq!(key_deserialized, key);
}
}
}