use std::io::{self, Read, Write};
use crate::{
constants::{PROOF_GENERATION_KEY_GENERATOR, SPENDING_KEY_GENERATOR},
keys::prf_expand,
};
use ff::PrimeField;
use group::{Group, GroupEncoding};
use subtle::CtOption;
use super::{NullifierDerivingKey, ProofGenerationKey, ViewingKey};
pub enum DecodingError {
LengthInvalid { expected: usize, actual: usize },
InvalidAsk,
InvalidNsk,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct OutgoingViewingKey(pub [u8; 32]);
#[derive(Clone)]
pub struct ExpandedSpendingKey {
pub ask: jubjub::Fr,
pub nsk: jubjub::Fr,
pub ovk: OutgoingViewingKey,
}
impl ExpandedSpendingKey {
pub fn from_spending_key(sk: &[u8]) -> Self {
let ask = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x00]).as_array());
let nsk = jubjub::Fr::from_bytes_wide(prf_expand(sk, &[0x01]).as_array());
let mut ovk = OutgoingViewingKey([0u8; 32]);
ovk.0
.copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]);
ExpandedSpendingKey { ask, nsk, ovk }
}
pub fn proof_generation_key(&self) -> ProofGenerationKey {
ProofGenerationKey {
ak: SPENDING_KEY_GENERATOR * self.ask,
nsk: self.nsk,
}
}
pub fn from_bytes(b: &[u8]) -> Result<Self, DecodingError> {
if b.len() != 96 {
return Err(DecodingError::LengthInvalid {
expected: 96,
actual: b.len(),
});
}
let ask = Option::from(jubjub::Fr::from_repr(b[0..32].try_into().unwrap()))
.ok_or(DecodingError::InvalidAsk)?;
let nsk = Option::from(jubjub::Fr::from_repr(b[32..64].try_into().unwrap()))
.ok_or(DecodingError::InvalidNsk)?;
let ovk = OutgoingViewingKey(b[64..96].try_into().unwrap());
Ok(ExpandedSpendingKey { ask, nsk, ovk })
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let mut repr = [0u8; 96];
reader.read_exact(repr.as_mut())?;
Self::from_bytes(&repr).map_err(|e| match e {
DecodingError::InvalidAsk => {
io::Error::new(io::ErrorKind::InvalidData, "ask not in field")
}
DecodingError::InvalidNsk => {
io::Error::new(io::ErrorKind::InvalidData, "nsk not in field")
}
DecodingError::LengthInvalid { .. } => unreachable!(),
})
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.to_bytes())
}
pub fn to_bytes(&self) -> [u8; 96] {
let mut result = [0u8; 96];
result[0..32].copy_from_slice(&self.ask.to_repr());
result[32..64].copy_from_slice(&self.nsk.to_repr());
result[64..96].copy_from_slice(&self.ovk.0);
result
}
}
#[derive(Debug)]
pub struct FullViewingKey {
pub vk: ViewingKey,
pub ovk: OutgoingViewingKey,
}
impl Clone for FullViewingKey {
fn clone(&self) -> Self {
FullViewingKey {
vk: ViewingKey {
ak: self.vk.ak,
nk: self.vk.nk,
},
ovk: self.ovk,
}
}
}
impl FullViewingKey {
pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey) -> Self {
FullViewingKey {
vk: ViewingKey {
ak: SPENDING_KEY_GENERATOR * expsk.ask,
nk: NullifierDerivingKey(PROOF_GENERATION_KEY_GENERATOR * expsk.nsk),
},
ovk: expsk.ovk,
}
}
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
let ak = {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
jubjub::SubgroupPoint::from_bytes(&buf).and_then(|p| CtOption::new(p, !p.is_identity()))
};
let nk = {
let mut buf = [0u8; 32];
reader.read_exact(&mut buf)?;
jubjub::SubgroupPoint::from_bytes(&buf)
};
if ak.is_none().into() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"ak not of prime order",
));
}
if nk.is_none().into() {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"nk not in prime-order subgroup",
));
}
let ak = ak.unwrap();
let nk = NullifierDerivingKey(nk.unwrap());
let mut ovk = [0u8; 32];
reader.read_exact(&mut ovk)?;
Ok(FullViewingKey {
vk: ViewingKey { ak, nk },
ovk: OutgoingViewingKey(ovk),
})
}
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
writer.write_all(&self.vk.ak.to_bytes())?;
writer.write_all(&self.vk.nk.0.to_bytes())?;
writer.write_all(&self.ovk.0)?;
Ok(())
}
pub fn to_bytes(&self) -> [u8; 96] {
let mut result = [0u8; 96];
self.write(&mut result[..])
.expect("should be able to serialize a FullViewingKey");
result
}
}
#[cfg(any(test, feature = "test-dependencies"))]
pub mod testing {
use proptest::collection::vec;
use proptest::prelude::*;
use std::fmt::{self, Debug, Formatter};
use super::{ExpandedSpendingKey, FullViewingKey};
impl Debug for ExpandedSpendingKey {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Spending keys cannot be Debug-formatted.")
}
}
prop_compose! {
pub fn arb_expanded_spending_key()(v in vec(any::<u8>(), 32..252)) -> ExpandedSpendingKey {
ExpandedSpendingKey::from_spending_key(&v)
}
}
prop_compose! {
pub fn arb_full_viewing_key()(sk in arb_expanded_spending_key()) -> FullViewingKey {
FullViewingKey::from_expanded_spending_key(&sk)
}
}
}
#[cfg(test)]
mod tests {
use group::{Group, GroupEncoding};
use super::FullViewingKey;
use crate::constants::SPENDING_KEY_GENERATOR;
#[test]
fn ak_must_be_prime_order() {
let mut buf = [0; 96];
let identity = jubjub::SubgroupPoint::identity();
buf[0..32].copy_from_slice(&identity.to_bytes());
buf[32..64].copy_from_slice(&identity.to_bytes());
assert_eq!(
FullViewingKey::read(&buf[..]).unwrap_err().to_string(),
"ak not of prime order"
);
let basepoint = SPENDING_KEY_GENERATOR;
buf[0..32].copy_from_slice(&basepoint.to_bytes());
assert!(FullViewingKey::read(&buf[..]).is_ok());
}
}