mod annihilative;
mod errors;
mod point;
mod pow;
#[cfg(any(feature = "convergent", feature = "divergent"))]
use ed25519_dalek::{SigningKey, VerifyingKey};
use hmac::{Hmac, Mac};
#[cfg(any(feature = "convergent", feature = "divergent"))]
use sha2::Digest;
use sha2::Sha256;
#[cfg(any(feature = "convergent", feature = "divergent"))]
use zeroize::Zeroize;
pub use crate::annihilative::AnnihilativeKey;
#[cfg(feature = "convergent")]
pub use crate::annihilative::Convergent;
#[cfg(feature = "divergent")]
pub use crate::annihilative::Divergent;
pub use crate::errors::AnnihilationError;
pub use crate::point::Point;
pub use crate::pow::ProofOfWork;
#[cfg(feature = "convergent")]
const CONVERGENT_DOMAIN_BYTE: u8 = 0x43;
#[cfg(feature = "divergent")]
const DIVERGENT_DOMAIN_BYTE: u8 = 0x44;
impl AnnihilativeKey {
pub fn new_pair(
key_material: &[u8],
antikey_material: &[u8],
constraint: u8,
) -> (Self, Self) {
let (key_solution, antikey_solution) =
ProofOfWork::mine(key_material, antikey_material, constraint);
let base_point = Point::shared_base(&key_solution, &antikey_solution);
let key = Self::new(&key_solution, base_point);
let antikey = Self::new(&antikey_solution, base_point);
(key, antikey)
}
pub fn verify(
&self,
counterpart: &Self,
) -> Result<[u8; 32], AnnihilationError> {
let (key, antikey) = match Self::validate_pair(&self, &counterpart) {
Ok(pair) => pair,
Err(e) => return Err(e),
};
let k_base = match Point::recover_base(&key) {
Ok(point) => *point.compress().as_bytes(),
Err(e) => return Err(e),
};
let a_base = match Point::recover_base(&antikey) {
Ok(point) => *point.compress().as_bytes(),
Err(e) => return Err(e),
};
let recovered_base = match k_base == a_base {
true => k_base,
false => return Err(AnnihilationError::PointMismatch),
};
let k_solution = &key.solution.to_bytes();
let a_solution = &antikey.solution.to_bytes();
let base_point = Point::shared_base(&k_solution, &a_solution);
if recovered_base != *base_point.compress().as_bytes() {
return Err(AnnihilationError::PointMismatch);
}
ProofOfWork::verify(&k_solution, &a_solution)
}
pub fn to_annihilation(
&self,
counterpart: &Self,
) -> Result<[u8; 32], AnnihilationError> {
let (key, antikey) = match Self::validate_pair(&self, &counterpart) {
Ok(pair) => pair,
Err(e) => return Err(e),
};
let artifact = match Self::verify(&key, &antikey) {
Ok(hash) => hash,
Err(e) => return Err(e),
};
let mut body_xor = [0u8; 22];
for i in 0..22 {
body_xor[i] = key.solution.body[i] ^ antikey.solution.body[i];
}
let k_point = key
.point
.decompress()
.expect("already verified key curve point should decompress");
let a_point = antikey
.point
.decompress()
.expect("already verified antikey curve point should decompress");
let point_sum = (k_point + a_point).compress();
let point_sum_bytes = point_sum.as_bytes();
let mut mac = Hmac::<Sha256>::new_from_slice(point_sum_bytes)
.expect("HMAC can take key of any size");
mac.update(&artifact);
mac.update(&body_xor);
Ok(mac.finalize().into_bytes().into())
}
pub fn into_annihilation(
self,
counterpart: Self,
) -> Result<[u8; 32], AnnihilationError> {
self.to_annihilation(&counterpart)
}
fn validate_pair<'a>(
&'a self,
counterpart: &'a Self,
) -> Result<(&'a AnnihilativeKey, &'a AnnihilativeKey), AnnihilationError>
{
let self_is_key = (self.solution.identity & 0x80) == 0;
let counterpart_is_key = (counterpart.solution.identity & 0x80) == 0;
if self_is_key == counterpart_is_key {
return Err(AnnihilationError::InvalidPair);
}
if self_is_key {
Ok((self, counterpart))
} else {
Ok((counterpart, self))
}
}
}
#[cfg(feature = "convergent")]
impl Convergent for AnnihilativeKey {
fn shared_signing_key(
&self,
context: Option<&[u8]>,
) -> Result<SigningKey, AnnihilationError> {
let base_point = match Point::recover_base(&self) {
Ok(point) => point,
Err(e) => return Err(e),
};
let context_bytes = context.unwrap_or(&[0u8; 16]);
let mut hasher = Sha256::new();
hasher.update(&[CONVERGENT_DOMAIN_BYTE]);
hasher.update(&context_bytes);
hasher.update(&[self.solution.constraint]);
hasher.update(base_point.compress().as_bytes());
let mut keying_material: [u8; 32] = hasher.finalize().into();
let signing_key = SigningKey::from_bytes(&keying_material);
keying_material.zeroize();
Ok(signing_key)
}
fn shared_verifying_key(
&self,
context: Option<&[u8]>,
) -> Result<VerifyingKey, AnnihilationError> {
let signing_key = self.shared_signing_key(context)?;
Ok(signing_key.verifying_key())
}
}
#[cfg(feature = "divergent")]
impl Divergent for AnnihilativeKey {
fn own_signing_key(&self, context: Option<&[u8]>) -> SigningKey {
let context_bytes = context.unwrap_or(&[0u8; 16]);
let mut hasher = Sha256::new();
hasher.update(&[DIVERGENT_DOMAIN_BYTE]);
hasher.update(&context_bytes);
hasher.update(&[self.solution.identity]);
hasher.update(&self.point.as_bytes());
let mut keying_material: [u8; 32] = hasher.finalize().into();
let signing_key = SigningKey::from_bytes(&keying_material);
keying_material.zeroize();
signing_key
}
fn own_verifying_key(&self, context: Option<&[u8]>) -> VerifyingKey {
let signing_key = self.own_signing_key(context);
signing_key.verifying_key()
}
}
#[cfg(test)]
mod tests {
use curve25519_dalek::edwards::CompressedEdwardsY;
use hex_literal::hex;
use crate::annihilative::AnnihilativeSolution;
use super::*;
fn test_key() -> AnnihilativeKey {
let solution = AnnihilativeSolution {
identity: 96,
commitment: 5764305080309989910,
body: hex!("f0ba820d877d17bde069485f536653c1acd2242d9a10"),
constraint: 16,
};
let point = CompressedEdwardsY::from_slice(&hex!(
"3c7f61f8aa2405f42d815e310a6091c7fb94df764c296a7c777cd108bdae7742"
))
.expect("hex string should represent curve point bytes");
AnnihilativeKey { solution, point }
}
fn test_antikey() -> AnnihilativeKey {
let solution = AnnihilativeSolution {
identity: 133,
commitment: 5230833101896872809,
body: hex!("f3ea7f7f9d35dac8904beec93fec5ff0b41e852b7d1b"),
constraint: 16,
};
let point = CompressedEdwardsY::from_slice(&hex!(
"9dd0d77e09d089fc4fff6c49c2d5c9f5b515369bf532caaf739a7e1ec3e6b3ec"
))
.expect("hex string should represent curve point bytes");
AnnihilativeKey { solution, point }
}
#[test]
fn test_new_pair() {
let (key, antikey) = AnnihilativeKey::new_pair(
b"some key material",
b"some antikey material",
16,
);
assert!(key.solution.identity <= 0x7F);
assert!(antikey.solution.identity >= 0x80);
let artifact = key.verify(&antikey);
assert!(artifact.is_ok());
}
#[test]
fn test_verify() {
let mut key = test_key();
let mut antikey = test_antikey();
let result = key.verify(&antikey);
assert!(result.is_ok());
let result = antikey.verify(&key);
assert!(result.is_ok());
let tmp_id = antikey.solution.identity;
antikey.solution.identity = key.solution.identity;
let result = key.verify(&antikey);
assert_eq!(result, Err(AnnihilationError::InvalidPair));
antikey.solution.identity = tmp_id;
let tmp_point = key.point;
key.point = CompressedEdwardsY(hex!(
"0202000000000000000000000000000000000000000000000000000000000000"
));
let result = key.verify(&antikey);
assert_eq!(result, Err(AnnihilationError::PointRecovery));
key.point = tmp_point;
let tmp_point = antikey.point;
antikey.point = CompressedEdwardsY(hex!(
"0202000000000000000000000000000000000000000000000000000000000000"
));
let result = antikey.verify(&key);
assert_eq!(result, Err(AnnihilationError::PointRecovery));
antikey.point = tmp_point;
let tmp_commit = antikey.solution.commitment;
antikey.solution.commitment = 2666156578537121652;
let result = key.verify(&antikey);
assert_eq!(result, Err(AnnihilationError::PointMismatch));
antikey.solution.commitment = tmp_commit;
for i in 0..22 {
antikey.solution.body[i] ^= 0xFF
}
let result = key.verify(&antikey);
assert_eq!(result, Err(AnnihilationError::PointMismatch));
}
#[test]
fn test_to_annihilation() {
let mut key = test_key();
let mut antikey = test_antikey();
let result_k = key.to_annihilation(&antikey);
let result_a = antikey.to_annihilation(&key);
assert!(result_k.is_ok());
assert!(result_a.is_ok());
assert_eq!(result_k.unwrap(), result_a.unwrap());
let tmp_id = antikey.solution.identity;
antikey.solution.identity = key.solution.identity;
let result = key.to_annihilation(&antikey);
assert_eq!(result, Err(AnnihilationError::InvalidPair));
antikey.solution.identity = tmp_id;
let tmp_point = key.point;
key.point = CompressedEdwardsY(hex!(
"0202000000000000000000000000000000000000000000000000000000000000"
));
let result = key.to_annihilation(&antikey);
assert_eq!(result, Err(AnnihilationError::PointRecovery));
key.point = tmp_point;
for i in 0..22 {
antikey.solution.body[i] ^= 0xFF
}
let result = key.to_annihilation(&antikey);
assert_eq!(result, Err(AnnihilationError::PointMismatch));
}
#[test]
fn test_into_annihilation() {
let key = test_key();
let antikey = test_antikey();
let result = key.into_annihilation(antikey);
assert!(result.is_ok());
}
#[cfg(feature = "convergent")]
#[test]
fn test_shared_signing_key() {
let mut key = test_key();
let antikey = test_antikey();
let key_signing = key.shared_signing_key(None).unwrap();
let antikey_signing = antikey.shared_signing_key(None).unwrap();
assert_eq!(key_signing, antikey_signing);
key.point = CompressedEdwardsY(hex!(
"0202000000000000000000000000000000000000000000000000000000000000"
));
let result = key.shared_signing_key(None);
assert_eq!(result, Err(AnnihilationError::PointRecovery));
}
#[cfg(feature = "convergent")]
#[test]
fn test_shared_verifying_key() {
let mut key = test_key();
let antikey = test_antikey();
let key_verifying = key.shared_verifying_key(None).unwrap();
let antikey_verifying = antikey.shared_verifying_key(None).unwrap();
assert_eq!(key_verifying, antikey_verifying);
key.point = CompressedEdwardsY(hex!(
"0202000000000000000000000000000000000000000000000000000000000000"
));
let result = key.shared_verifying_key(None);
assert_eq!(result, Err(AnnihilationError::PointRecovery));
}
#[cfg(feature = "divergent")]
#[test]
fn test_own_signing_key() {
let key = test_key();
let antikey = test_antikey();
let key_signing = key.own_signing_key(None);
let antikey_signing = antikey.own_signing_key(None);
assert_ne!(key_signing, antikey_signing);
}
#[cfg(feature = "divergent")]
#[test]
fn test_own_verifying_key() {
let key = test_key();
let antikey = test_antikey();
let key_verifying = key.own_verifying_key(None);
let antikey_verifying = antikey.own_verifying_key(None);
assert_ne!(key_verifying, antikey_verifying);
}
}