use crate::sign::{InnerSignature, HASH_HEAD};
use crate::{
EdwardsPoint, Scalar, ScalarBytes, SecretKey, SigningError, VerifyingKey, WideScalarBytes,
SECRET_KEY_LENGTH,
};
use sha3::digest::ExtendableOutputReset;
use sha3::{
digest::{ExtendableOutput, Update, XofReader},
Shake256,
};
use zeroize::{Zeroize, ZeroizeOnDrop};
#[derive(Clone)]
pub struct ExpandedSecretKey {
pub(crate) seed: SecretKey,
pub(crate) scalar: Scalar,
pub(crate) public_key: VerifyingKey,
pub(crate) hash_prefix: ScalarBytes,
}
impl Zeroize for ExpandedSecretKey {
fn zeroize(&mut self) {
self.seed.zeroize();
self.scalar.zeroize();
self.hash_prefix.zeroize();
}
}
impl Drop for ExpandedSecretKey {
fn drop(&mut self) {
self.zeroize();
}
}
impl From<&SecretKey> for ExpandedSecretKey {
fn from(secret_key: &SecretKey) -> Self {
Self::from_seed(secret_key)
}
}
impl ZeroizeOnDrop for ExpandedSecretKey {}
impl ExpandedSecretKey {
pub fn from_seed(seed: &SecretKey) -> Self {
let mut reader = Shake256::default().chain(seed).finalize_xof();
let mut bytes = WideScalarBytes::default();
reader.read(&mut bytes);
let mut scalar_bytes = ScalarBytes::default();
scalar_bytes.copy_from_slice(&bytes[..SECRET_KEY_LENGTH]);
scalar_bytes[0] &= 0xFC;
scalar_bytes[56] = 0;
scalar_bytes[55] |= 0x80;
let scalar = Scalar::from_bytes_mod_order(&scalar_bytes);
let mut hash_prefix = ScalarBytes::default();
hash_prefix.copy_from_slice(&bytes[SECRET_KEY_LENGTH..]);
let point = EdwardsPoint::GENERATOR * scalar;
let public_key = VerifyingKey {
compressed: point.compress(),
point,
};
Self {
seed: *seed,
scalar,
public_key,
hash_prefix,
}
}
pub fn sign_raw(&self, m: &[u8]) -> Result<InnerSignature, SigningError> {
self.sign_inner(0, &[], m)
}
pub fn sign_ctx(&self, ctx: &[u8], m: &[u8]) -> Result<InnerSignature, SigningError> {
self.sign_inner(0, ctx, m)
}
pub fn sign_prehashed(&self, ctx: &[u8], m: &[u8]) -> Result<InnerSignature, SigningError> {
self.sign_inner(1, ctx, m)
}
fn sign_inner(&self, phflag: u8, ctx: &[u8], m: &[u8]) -> Result<InnerSignature, SigningError> {
if ctx.len() > 255 {
return Err(SigningError::PrehashedContextLength);
}
let clen = ctx.len() as u8;
let mut reader = Shake256::default()
.chain(HASH_HEAD)
.chain([phflag])
.chain([clen])
.chain(ctx)
.chain(self.hash_prefix)
.chain(m)
.finalize_xof_reset();
let mut bytes = WideScalarBytes::default();
reader.read(&mut bytes);
let r = Scalar::from_bytes_mod_order_wide(&bytes);
let big_r = EdwardsPoint::GENERATOR * r;
let compressed_r = big_r.compress();
reader = Shake256::default()
.chain(HASH_HEAD)
.chain([phflag])
.chain([clen])
.chain(ctx)
.chain(compressed_r.as_bytes())
.chain(self.public_key.compressed.as_bytes())
.chain(m)
.finalize_xof();
reader.read(&mut bytes);
let k = Scalar::from_bytes_mod_order_wide(&bytes);
Ok(InnerSignature {
r: big_r,
s: r + k * self.scalar,
})
}
}