use rand::prelude::*;
use rand_chacha::ChaCha20Rng;
use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;
use crate::signature::{HashType, SignatureScheme};
use crate::utils::hash;
pub type BasicLamportKey = [[[u8; 32]; 2]; 256];
#[derive(PartialEq, Serialize, Deserialize)]
pub struct BasicLamportSignature {
#[serde(with = "BigArray")]
preimages: [[u8; 32]; 256],
}
#[derive(Clone)]
pub struct BasicLamportSignatureScheme {
sk: BasicLamportKey,
pk: BasicLamportKey,
message: Option<HashType>,
}
impl BasicLamportSignatureScheme {
pub fn new(seed: [u8; 32]) -> Self {
let mut rng = ChaCha20Rng::from_seed(seed);
let mut sk = [[[0; 32]; 2]; 256];
for bit_to_sign in 0..256 {
for bit in 0..2 {
rng.fill_bytes(&mut sk[bit_to_sign][bit]);
}
}
let mut pk = [[[0; 32]; 2]; 256];
for bit_to_sign in 0..256 {
for bit in 0..2 {
pk[bit_to_sign][bit] = hash(&sk[bit_to_sign][bit]);
}
}
Self {
pk,
sk,
message: None,
}
}
}
impl SignatureScheme<BasicLamportKey, HashType, BasicLamportSignature>
for BasicLamportSignatureScheme
{
fn public_key(&self) -> BasicLamportKey {
self.pk
}
fn sign(&mut self, message: HashType) -> BasicLamportSignature {
if let Some(existing_message) = self.message {
if existing_message != message {
panic!("One-time signature has been used to sign more than one message!")
}
}
self.message = Some(message);
let mut signature: [[u8; 32]; 256] = [[0; 32]; 256];
for byte_index in 0..32 {
let byte = message[byte_index];
for local_bit_index in 0..8 {
let bit_index = byte_index * 8 + local_bit_index;
if byte & (1 << local_bit_index) != 0 {
signature[bit_index] = self.sk[bit_index][1];
} else {
signature[bit_index] = self.sk[bit_index][0];
}
}
}
BasicLamportSignature {
preimages: signature,
}
}
fn verify(pk: BasicLamportKey, message: HashType, signature: &BasicLamportSignature) -> bool {
let mut is_correct = true;
for byte_index in 0..32 {
let byte = message[byte_index];
for local_bit_index in 0..8 {
let bit_index = byte_index * 8 + local_bit_index;
let hash = hash(&signature.preimages[bit_index]);
let pk_index_to_expect = (byte & (1 << local_bit_index) != 0) as usize;
is_correct &= hash == pk[bit_index][pk_index_to_expect];
}
}
is_correct
}
}
#[cfg(test)]
mod tests {
use crate::signature::basic_lamport::{BasicLamportSignature, BasicLamportSignatureScheme};
use crate::signature::SignatureScheme;
fn get_signature_scheme() -> BasicLamportSignatureScheme {
let seed = [0u8; 32];
BasicLamportSignatureScheme::new(seed)
}
#[test]
fn test_correct_signature() {
let mut signature_scheme = get_signature_scheme();
let signature = signature_scheme.sign([1u8; 32]);
assert!(BasicLamportSignatureScheme::verify(
signature_scheme.public_key(),
[1u8; 32],
&signature
))
}
#[test]
fn test_incorrect_signature() {
let signature_scheme = get_signature_scheme();
let incorrect_signature = BasicLamportSignature {
preimages: [[0u8; 32]; 256],
};
assert!(!BasicLamportSignatureScheme::verify(
signature_scheme.public_key(),
[1u8; 32],
&incorrect_signature
))
}
#[test]
fn test_can_sign_same_message() {
let mut signature_scheme = get_signature_scheme();
signature_scheme.sign([1u8; 32]);
signature_scheme.sign([1u8; 32]);
}
#[test]
#[should_panic]
fn test_cant_sign_different_messages() {
let mut signature_scheme = get_signature_scheme();
signature_scheme.sign([1u8; 32]);
signature_scheme.sign([2u8; 32]);
}
}