mirror of
https://github.com/mii443/lamport_sigs.rs.git
synced 2025-08-22 06:55:34 +00:00
Initial commit.
This commit is contained in:
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
target
|
||||
Cargo.lock
|
8
Cargo.toml
Normal file
8
Cargo.toml
Normal file
@ -0,0 +1,8 @@
|
||||
[package]
|
||||
name = "lamport"
|
||||
version = "0.1.0"
|
||||
authors = ["Frederic Jacobs <github@fredericjacobs.com>"]
|
||||
|
||||
[dependencies]
|
||||
rust-crypto = "^0.2.36"
|
||||
rand = "^0.3"
|
197
src/lib.rs
Normal file
197
src/lib.rs
Normal file
@ -0,0 +1,197 @@
|
||||
pub extern crate crypto;
|
||||
extern crate rand;
|
||||
|
||||
use rand::OsRng;
|
||||
use rand::Rng;
|
||||
use crypto::digest::Digest;
|
||||
use std::hash::{Hash, Hasher};
|
||||
|
||||
pub struct PublicKey<T: Digest + Clone> {
|
||||
zero_values: Vec<Vec<u8>>,
|
||||
one_values: Vec<Vec<u8>>,
|
||||
digest: T
|
||||
}
|
||||
|
||||
pub struct PrivateKey<T: Digest + Clone> {
|
||||
zero_values: Vec<Vec<u8>>, // For a n bits hash function: (n * n/8 bytes) for zero_values and one_values
|
||||
one_values: Vec<Vec<u8>>,
|
||||
digest: T,
|
||||
used: bool
|
||||
}
|
||||
|
||||
impl<D: Digest + Clone> Hash for PrivateKey<D> {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
let public_key = self.public_key();
|
||||
public_key.one_values.hash(state);
|
||||
public_key.zero_values.hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn verify_signature<T: Digest + Clone>( signature: &Vec<Vec<u8>>,
|
||||
data:&[u8],
|
||||
public_key: &PublicKey<T>) -> bool
|
||||
{
|
||||
|
||||
let mut digest = public_key.digest.clone();
|
||||
digest.input(data);
|
||||
let mut data_hash = vec![0 as u8; digest.output_bytes()];
|
||||
digest.result(data_hash.as_mut_slice());
|
||||
digest.reset();
|
||||
|
||||
for i in 0..data_hash.len() {
|
||||
let byte = data_hash[i];
|
||||
for j in 0..8 {
|
||||
let offset = i*8 + j;
|
||||
if (byte & (1<<j)) > 0 {
|
||||
digest.input(signature[offset].as_slice());
|
||||
let mut hashed_value = vec![0 as u8; digest.output_bytes()];
|
||||
digest.result(hashed_value.as_mut_slice());
|
||||
digest.reset();
|
||||
if hashed_value != public_key.one_values[offset] {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
digest.input(signature[offset].as_slice());
|
||||
let mut hashed_value = vec![0 as u8; digest.output_bytes()];
|
||||
digest.result(hashed_value.as_mut_slice());
|
||||
digest.reset();
|
||||
if hashed_value != public_key.zero_values[offset] {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
impl <T: Digest + Clone> PrivateKey<T> {
|
||||
pub fn new(digest: T) -> PrivateKey<T> {
|
||||
let generate_bit_hash_values = |hasher: &T| -> Vec<Vec<u8>> {
|
||||
let mut rng = match OsRng::new() {
|
||||
Ok(g) => g,
|
||||
Err(e) => panic!("Failed to obtain OS RNG: {}", e)
|
||||
};
|
||||
let buffer_byte = vec![0 as u8; hasher.output_bytes()];
|
||||
let mut buffer = vec![buffer_byte; hasher.output_bits()];
|
||||
|
||||
for hash in buffer.iter_mut() {
|
||||
rng.fill_bytes(hash)
|
||||
}
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
let zero_values = generate_bit_hash_values(&digest);
|
||||
let one_values = generate_bit_hash_values(&digest);
|
||||
|
||||
return PrivateKey { zero_values: zero_values,
|
||||
one_values: one_values,
|
||||
digest: digest,
|
||||
used: false }
|
||||
}
|
||||
|
||||
pub fn public_key(&self) -> PublicKey<T> {
|
||||
let mut digest = self.digest.clone();
|
||||
|
||||
let hash_values = |x: &Vec<Vec<u8>>, hash_func: &mut Digest | -> Vec<Vec<u8>> {
|
||||
let buffer_byte = vec![0 as u8; hash_func.output_bytes()];
|
||||
let mut buffer = vec![buffer_byte; hash_func.output_bits()];
|
||||
|
||||
for i in 0..hash_func.output_bits(){
|
||||
hash_func.input(x[i].as_slice());
|
||||
hash_func.result(buffer[i].as_mut_slice());
|
||||
hash_func.reset();
|
||||
}
|
||||
|
||||
return buffer;
|
||||
};
|
||||
|
||||
let hashed_zero_values = hash_values(&self.zero_values, &mut digest);
|
||||
let hashed_one_values = hash_values(&self.one_values, &mut digest);
|
||||
|
||||
return PublicKey { zero_values: hashed_zero_values,
|
||||
one_values: hashed_one_values,
|
||||
digest: digest }
|
||||
}
|
||||
pub fn sign(&mut self, data: &[u8]) -> Result<Vec<Vec<u8>>, &'static str> {
|
||||
if self.used {
|
||||
return Err("Attempting to sign more than once.");
|
||||
}
|
||||
self.digest.input(data);
|
||||
let mut data_hash = vec![0 as u8; self.digest.output_bytes()];
|
||||
self.digest.result(data_hash.as_mut_slice());
|
||||
self.digest.reset();
|
||||
|
||||
let mut signature = Vec::new();
|
||||
|
||||
for i in 0..data_hash.len() {
|
||||
let byte = data_hash[i];
|
||||
for j in 0..8 {
|
||||
let offset = i*8 + j;
|
||||
if (byte & (1<<j)) > 0 {
|
||||
// Bit is 1
|
||||
signature.push(self.one_values[offset].clone());
|
||||
} else {
|
||||
// Bit is 0
|
||||
signature.push(self.zero_values[offset].clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
self.used = true;
|
||||
return Ok(signature);
|
||||
}
|
||||
}
|
||||
|
||||
impl <T: Digest + Clone> Drop for PrivateKey<T> {
|
||||
fn drop(&mut self) {
|
||||
let zeroize_vector = |vector: &mut Vec<Vec<u8>>| {
|
||||
for v2 in vector.iter_mut() {
|
||||
for byte in v2.iter_mut() {
|
||||
*byte = 0;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
zeroize_vector(&mut self.zero_values);
|
||||
zeroize_vector(&mut self.one_values);
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Digest + Clone> PartialEq for PrivateKey<T> {
|
||||
// ⚠️ This is not a constant-time implementation
|
||||
fn eq(&self, other: &PrivateKey<T>) -> bool {
|
||||
if self.one_values.len() != other.one_values.len() {
|
||||
return false;
|
||||
}
|
||||
if self.zero_values.len() != other.zero_values.len() {
|
||||
return false;
|
||||
}
|
||||
|
||||
for i in 0..self.zero_values.len() {
|
||||
if self.zero_values[i] != self.zero_values[i] || self.one_values[i] != other.one_values[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use crypto::sha3::Sha3;
|
||||
#[test]
|
||||
fn test_public_key_length_256() {
|
||||
let pk = PrivateKey::new(Sha3::sha3_256());
|
||||
assert!( pk.public_key().one_values.len() == 256 &&
|
||||
pk.public_key().zero_values.len() == 256);
|
||||
}
|
||||
#[test]
|
||||
fn test_public_key_length_512() {
|
||||
let pk = PrivateKey::new(Sha3::sha3_512());
|
||||
assert!( pk.public_key().one_values.len() == 512 &&
|
||||
pk.public_key().zero_values.len() == 512);
|
||||
}
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
pub mod test;
|
36
src/test.rs
Normal file
36
src/test.rs
Normal file
@ -0,0 +1,36 @@
|
||||
use crypto::sha3::Sha3;
|
||||
use PrivateKey;
|
||||
use verify_signature;
|
||||
|
||||
#[cfg(test)]
|
||||
#[test]
|
||||
fn test_distinctive_successive_keygen() {
|
||||
let mut past_buff = PrivateKey::new(Sha3::sha3_256());
|
||||
for _ in 0..100 {
|
||||
let buffer = PrivateKey::new(Sha3::sha3_256());
|
||||
assert!(past_buff != buffer);
|
||||
past_buff = buffer;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_verif() {
|
||||
let mut priv_key = PrivateKey::new(Sha3::sha3_256());
|
||||
let data = "Hello World".as_bytes();
|
||||
let signature = priv_key.sign(data).unwrap();
|
||||
|
||||
let pub_key = priv_key.public_key();
|
||||
|
||||
assert!(verify_signature(&signature, data, &pub_key));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_verif_fail() {
|
||||
let mut priv_key = PrivateKey::new(Sha3::sha3_256());
|
||||
let data = "Hello Word".as_bytes();
|
||||
let signature = priv_key.sign(data).unwrap();
|
||||
|
||||
let pub_key = priv_key.public_key();
|
||||
let data2 = "Hello".as_bytes();
|
||||
assert!(!verify_signature(&signature, data2, &pub_key));
|
||||
}
|
7
tokamak.toml
Normal file
7
tokamak.toml
Normal file
@ -0,0 +1,7 @@
|
||||
[helper]
|
||||
path = "" # Reserved for future helper path and configurations
|
||||
[options]
|
||||
save_buffers_before_run = true # Saving buffers before every cargo command run
|
||||
general_warnings = true # Show general warnings
|
||||
[project]
|
||||
auto_format_timing = 5 # Run auto formatting for project for specified interval (seconds)
|
Reference in New Issue
Block a user