mirror of
https://github.com/saymrwulf/fips205-source.git
synced 2026-09-04 20:03:45 +00:00
algs a6
This commit is contained in:
parent
713d32f92a
commit
0c9f00e8a9
5 changed files with 202 additions and 55 deletions
|
|
@ -11,6 +11,8 @@ rust-version = "1.73"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
|
zeroize = { version = "1.6.0", features = ["zeroize_derive"] }
|
||||||
rand_core = { version = "0.6.4", default-features = false }
|
rand_core = { version = "0.6.4", default-features = false }
|
||||||
|
sha3 = { version = "0.10.8", default-features = false }
|
||||||
|
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["default-rng", "slh_dsa_sha2_128s", "slh_dsa_shake_128s", "slh_dsa_sha2_128f", "slh_dsa_shake_128f",
|
default = ["default-rng", "slh_dsa_sha2_128s", "slh_dsa_shake_128s", "slh_dsa_sha2_128f", "slh_dsa_shake_128f",
|
||||||
|
|
|
||||||
187
src/algs.rs
187
src/algs.rs
|
|
@ -1,7 +1,12 @@
|
||||||
|
use crate::types::ADRS;
|
||||||
|
use crate::types::{WOTS_PK, WOTS_PRF};
|
||||||
|
use crate::Context;
|
||||||
use alloc::vec;
|
use alloc::vec;
|
||||||
use alloc::vec::Vec;
|
use alloc::vec::Vec;
|
||||||
use crate::Params;
|
use sha3::{
|
||||||
use crate::traits::PK;
|
digest::{ExtendableOutput, Update, XofReader},
|
||||||
|
Shake256,
|
||||||
|
};
|
||||||
|
|
||||||
/// Algorithm 1: `toInt(X, n)` on page 14.
|
/// Algorithm 1: `toInt(X, n)` on page 14.
|
||||||
/// Convert a byte string to an integer.
|
/// Convert a byte string to an integer.
|
||||||
|
|
@ -91,29 +96,50 @@ pub(crate) fn base_2b(x: &[u8], b: u32, out_len: usize) -> Vec<u64> {
|
||||||
baseb
|
baseb
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) fn shake256(input: &[&[u8]]) -> [u8; 32] {
|
||||||
|
let mut hasher = Shake256::default();
|
||||||
|
input.iter().for_each(|item| hasher.update(item));
|
||||||
|
let mut reader = hasher.finalize_xof();
|
||||||
|
let mut result = [0u8; 32];
|
||||||
|
reader.read(&mut result);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn f(pk_seed: &[u8], adrs: &ADRS, tmp: &[u8]) -> Vec<u8> {
|
||||||
|
shake256(&[&pk_seed, &adrs.to_bytes(), tmp]).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Algorithm 4: `chain(X, i, s, PK.seed, ADRS)` on page 17.
|
/// Algorithm 4: `chain(X, i, s, PK.seed, ADRS)` on page 17.
|
||||||
/// Chaining function used in WOTS+.
|
/// Chaining function used in WOTS+. The chain function takes as input an n-byte string `X` and integers `s` and `i`
|
||||||
|
/// and returns the result of iterating a hash function `F` on the input `s` times, starting from an index of `i`.
|
||||||
|
/// The chain function also requires as input PK.seed, which is part of the SLH-DSA public key, and an address `ADRS`.
|
||||||
|
/// The type in `ADRS` must be set to `WOTS_HASH`, and the layer address, tree address, key pair address, and chain
|
||||||
|
/// address must be set to the address of the chain being computed. The chain function updates the hash address in
|
||||||
|
/// `ADRS` with each iteration to specify the current position in the chain prior to ADRS’s use in `F`.
|
||||||
///
|
///
|
||||||
/// Input: Input string `X`, start index `i`, number of steps `s`, public seed `PK.seed`, address `ADRS`. <br>
|
/// Input: Input string `X`, start index `i`, number of steps `s`, public seed `PK.seed`, address `ADRS`. <br>
|
||||||
/// Output: Value of `F` iterated `s` times on `X`.
|
/// Output: Value of `F` iterated `s` times on `X`.
|
||||||
pub(crate) fn chain(params: &Params, _x: &[u8], i: usize, s: usize, pk: &impl PK, _adrs: u32) -> Option<[u8; 32]> {
|
pub(crate) fn chain(
|
||||||
|
context: &Context, cap_x: Vec<u8>, i: usize, s: usize, pk_seed: &[u8], adrs: &mut ADRS,
|
||||||
|
) -> Option<Vec<u8>> {
|
||||||
// 1: if (i + s) ≥ w then
|
// 1: if (i + s) ≥ w then
|
||||||
if (i + s) >= params.w {
|
if (i + s) >= context.w {
|
||||||
// 2: return NULL
|
// 2: return NULL
|
||||||
return None;
|
return None;
|
||||||
// 3: end if
|
// 3: end if
|
||||||
}
|
}
|
||||||
// 4:
|
// 4:
|
||||||
// 5: tmp ← X
|
// 5: tmp ← X
|
||||||
let mut tmp = [0u8; 32]; // Check digest width
|
let mut tmp = cap_x;
|
||||||
// 6:
|
// 6:
|
||||||
// 7: for j from i to i + s − 1 do
|
// 7: for j from i to i + s − 1 do
|
||||||
for j in i..(i+s) {
|
for j in i..(i + s) {
|
||||||
// 8: ADRS.setHashAddress(j)
|
// 8: ADRS.setHashAddress(j)
|
||||||
|
adrs.set_hash_address(j.try_into().expect("usize->u32 fails"));
|
||||||
// 9: tmp ← F(PK.seed, ADRS, tmp)
|
// 9: tmp ← F(PK.seed, ADRS, tmp)
|
||||||
|
tmp = f(&pk_seed, &adrs, &tmp)
|
||||||
// 10: end for
|
// 10: end for
|
||||||
}
|
}
|
||||||
// 11: return tmp
|
// 11: return tmp
|
||||||
|
|
@ -121,26 +147,82 @@ pub(crate) fn chain(params: &Params, _x: &[u8], i: usize, s: usize, pk: &impl PK
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
pub(crate) fn prf(pk_seed: &[u8], sk_seed: &[u8], adrs: &ADRS) -> Vec<u8> {
|
||||||
|
shake256(&[&pk_seed, &sk_seed, &adrs.to_bytes()]).into()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Note: duplicates a bunch of shake256 due to `ml` vec<option<vec<u8>>> type 'challenges'. TODO: improve
|
||||||
|
pub(crate) fn tlen(
|
||||||
|
context: &Context, pk_seed: &[u8], adrs: &ADRS, ml: Vec<Option<Vec<u8>>>,
|
||||||
|
) -> Vec<u8> {
|
||||||
|
assert!(ml
|
||||||
|
.iter()
|
||||||
|
.all(|item| item.is_some() & (item.as_ref().unwrap().len() == context.len1)));
|
||||||
|
let mut hasher = Shake256::default();
|
||||||
|
hasher.update(pk_seed);
|
||||||
|
hasher.update(&adrs.to_bytes());
|
||||||
|
ml.iter()
|
||||||
|
.for_each(|item| hasher.update(&item.as_ref().unwrap()));
|
||||||
|
let mut reader = hasher.finalize_xof();
|
||||||
|
let mut result = [0u8; 32];
|
||||||
|
reader.read(&mut result);
|
||||||
|
result.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Algorithm 5: `wots_PKgen(SK.seed, PK.seed, ADRS)` on page 18.
|
/// Algorithm 5: `wots_PKgen(SK.seed, PK.seed, ADRS)` on page 18.
|
||||||
/// Generate a WOTS+ public key.
|
/// Generate a WOTS+ public key. The `wots_PKgen` function generates WOTS+ public keys. It takes as input `SK.seed`
|
||||||
|
/// and `PK.seed` from the SLH-DSA private key and an address. The type in the address `ADRS` must be set to
|
||||||
|
/// `WOTS_HASH`, and the layer address, tree address, and key pair address must encode the address of the `WOTS+`
|
||||||
|
/// public key to be generated.
|
||||||
///
|
///
|
||||||
/// Input: Secret seed `SK.seed`, public seed `PK.seed`, address `ADRS`. <br>
|
/// Input: Secret seed `SK.seed`, public seed `PK.seed`, address `ADRS`. <br>
|
||||||
/// Output: WOTS+ public key `pk`.
|
/// Output: WOTS+ public key `pk`.
|
||||||
const _A5: u32 = 0;
|
pub(crate) fn wots_pkgen(
|
||||||
// 1: skADRS ← ADRS ▷ Copy address to create key generation key address
|
context: &Context, sk_seed: &[u8], pk_seed: &[u8], adrs: &mut ADRS,
|
||||||
// 2: skADRS.setTypeAndClear(WOTS_PRF)
|
) -> Vec<u8> {
|
||||||
// 3: skADRS.setKeyPairAddress(ADRS.getKeyPairAddress())
|
// 1: skADRS ← ADRS ▷ Copy address to create key generation key address
|
||||||
// 4: for i from 0 to len − 1 do
|
let mut sk_adrs = adrs.clone();
|
||||||
// 5: skADRS.setChainAddress(i)
|
|
||||||
// 6: sk ← PRF(PK.seed, SK.seed, skADRS) ▷ Compute secret value for chain i
|
// 2: skADRS.setTypeAndClear(WOTS_PRF)
|
||||||
// 7: ADRS.setChainAddress(i)
|
sk_adrs.set_type_and_clear(WOTS_PRF);
|
||||||
// 8: tmp[i] ← chain(sk, 0, w − 1, PK.seed, ADRS) ▷ Compute public value for chain i
|
|
||||||
// 9: end for
|
// 3: skADRS.setKeyPairAddress(ADRS.getKeyPairAddress())
|
||||||
// 10: wotspkADRS ← ADRS ▷ Copy address to create WOTS+ public key address
|
sk_adrs.set_key_pair_address(adrs.get_key_pair_address());
|
||||||
// 11: wotspkADRS.setTypeAndClear(WOTS_PK)
|
|
||||||
// 12: wotspkADRS.setKeyPairAddress(ADRS.getKeyPairAddress())
|
// 4: for i from 0 to len − 1 do
|
||||||
// 13: pk ← Tlen (PK.seed, wotspkADRS,tmp) ▷ Compress public key
|
let mut tmp: Vec<Option<Vec<u8>>> = vec![];
|
||||||
// 14: return pk
|
for i in 0..context.len1 {
|
||||||
|
// 5: skADRS.setChainAddress(i)
|
||||||
|
sk_adrs.set_chain_address(i);
|
||||||
|
|
||||||
|
// 6: sk ← PRF(PK.seed, SK.seed, skADRS) ▷ Compute secret value for chain i
|
||||||
|
let sk = prf(pk_seed, sk_seed, &sk_adrs);
|
||||||
|
|
||||||
|
// 7: ADRS.setChainAddress(i)
|
||||||
|
adrs.set_chain_address(i);
|
||||||
|
|
||||||
|
// 8: tmp[i] ← chain(sk, 0, w − 1, PK.seed, ADRS) ▷ Compute public value for chain i
|
||||||
|
tmp[i] = chain(context, sk, 0, context.w - 1, pk_seed, adrs);
|
||||||
|
|
||||||
|
// 9: end for
|
||||||
|
}
|
||||||
|
// 10: wotspkADRS ← ADRS ▷ Copy address to create WOTS+ public key address
|
||||||
|
let mut wotspk_adrs = adrs.clone();
|
||||||
|
|
||||||
|
// 11: wotspkADRS.setTypeAndClear(WOTS_PK)
|
||||||
|
wotspk_adrs.set_type_and_clear(WOTS_PK);
|
||||||
|
|
||||||
|
// 12: wotspkADRS.setKeyPairAddress(ADRS.getKeyPairAddress())
|
||||||
|
wotspk_adrs.set_key_pair_address(adrs.get_key_pair_address());
|
||||||
|
|
||||||
|
// 13: pk ← Tlen (PK.seed, wotspkADRS,tmp) ▷ Compress public key
|
||||||
|
let pk = tlen(context, pk_seed, &wotspk_adrs, tmp);
|
||||||
|
|
||||||
|
// 14: return pk
|
||||||
|
pk
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// Algorithm 6: `wots_sign(M, SK.seed, PK.seed, ADRS)` on page 19.
|
/// Algorithm 6: `wots_sign(M, SK.seed, PK.seed, ADRS)` on page 19.
|
||||||
|
|
@ -148,29 +230,40 @@ const _A5: u32 = 0;
|
||||||
///
|
///
|
||||||
/// Input: Message `M`, secret seed `SK.seed`, public seed `PK.seed`, address `ADRS`. <br>
|
/// Input: Message `M`, secret seed `SK.seed`, public seed `PK.seed`, address `ADRS`. <br>
|
||||||
/// Output: WOTS+ signature sig.
|
/// Output: WOTS+ signature sig.
|
||||||
const _A6: u32 = 0;
|
pub(crate) fn wots_sign(context: &Context, m: &[u8], _sk_seed: &[u8], _pk_seed: &[u8], _adrs: &ADRS) -> u32 {
|
||||||
// 1: csum ← 0
|
// 1: csum ← 0
|
||||||
// 2:
|
let mut csum = 0u64;
|
||||||
// 3: msg ← base_2b (M, lgw, len1) ▷ Convert message to base w
|
|
||||||
// 4:
|
|
||||||
// 5: for i from 0 to len1 − 1 do ▷ Compute checksum
|
|
||||||
// 6: csum ← csum + w − 1 − msg[i]
|
|
||||||
// 7: end for
|
|
||||||
// 8:
|
|
||||||
// 9: csum ← csum ≪ ((8 − ((len2·lgw) mod 8)) mod 8) ▷ For lgw = 4 left shift by 4
|
|
||||||
// 10: msg ← msg ∥ base_2^b(toByte(csum, ceil(len2·lgw/8)), lgw, len2) ▷ Convert csum to base w
|
|
||||||
// 11:
|
|
||||||
// 12: skADRS ← ADRS
|
|
||||||
// 13: skADRS.setTypeAndClear(WOTS_PRF)
|
|
||||||
// 14: skADRS.setKeyPairAddress(ADRS.getKeyPairAddress())
|
|
||||||
// 15: for i from 0 to len − 1 do
|
|
||||||
// 16: skADRS.setChainAddress(i)
|
|
||||||
// 17: sk ← PRF(PK.seed, SK.seed, skADRS) ▷ Compute secret value for chain i
|
|
||||||
// 18: ADRS.setChainAddress(i)
|
|
||||||
// 19: sig[i] ← chain(sk, 0, msg[i], PK.seed, ADRS) ▷ Compute signature value for chain i
|
|
||||||
// 20: end for
|
|
||||||
// 21: return sig
|
|
||||||
|
|
||||||
|
// 2:
|
||||||
|
// 3: msg ← base_2b(M, lgw, len1) ▷ Convert message to base w
|
||||||
|
let msg = base_2b(m, context.lgw, context.len1);
|
||||||
|
|
||||||
|
// 4:
|
||||||
|
// 5: for i from 0 to len1 − 1 do ▷ Compute checksum
|
||||||
|
for i in 0..context.len1 {
|
||||||
|
|
||||||
|
// 6: csum ← csum + w − 1 − msg[i]
|
||||||
|
csum += context.w as u64 - 1 - msg[i];
|
||||||
|
|
||||||
|
// 7: end for
|
||||||
|
}
|
||||||
|
// 8:
|
||||||
|
// 9: csum ← csum ≪ ((8 − ((len2·lgw) mod 8)) mod 8) ▷ For lgw = 4 left shift by 4
|
||||||
|
|
||||||
|
// 10: msg ← msg ∥ base_2^b(toByte(csum, ceil(len2·lgw/8)), lgw, len2) ▷ Convert csum to base w
|
||||||
|
// 11:
|
||||||
|
// 12: skADRS ← ADRS
|
||||||
|
// 13: skADRS.setTypeAndClear(WOTS_PRF)
|
||||||
|
// 14: skADRS.setKeyPairAddress(ADRS.getKeyPairAddress())
|
||||||
|
// 15: for i from 0 to len − 1 do
|
||||||
|
// 16: skADRS.setChainAddress(i)
|
||||||
|
// 17: sk ← PRF(PK.seed, SK.seed, skADRS) ▷ Compute secret value for chain i
|
||||||
|
// 18: ADRS.setChainAddress(i)
|
||||||
|
// 19: sig[i] ← chain(sk, 0, msg[i], PK.seed, ADRS) ▷ Compute signature value for chain i
|
||||||
|
// 20: end for
|
||||||
|
// 21: return sig
|
||||||
|
csum as u32
|
||||||
|
}
|
||||||
|
|
||||||
/// Algorithm 7: `wots_PKFromSig(sig, M, PK.seed, ADRS)` on page 20.
|
/// Algorithm 7: `wots_PKFromSig(sig, M, PK.seed, ADRS)` on page 20.
|
||||||
/// Compute a WOTS+ public key from a message and its signature.
|
/// Compute a WOTS+ public key from a message and its signature.
|
||||||
|
|
|
||||||
16
src/lib.rs
16
src/lib.rs
|
|
@ -10,21 +10,27 @@ extern crate alloc;
|
||||||
|
|
||||||
mod algs;
|
mod algs;
|
||||||
mod traits;
|
mod traits;
|
||||||
|
mod types;
|
||||||
|
|
||||||
/// to be deleted
|
/// to be deleted
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn add(left: usize, right: usize) -> usize { left + right }
|
pub fn add(left: usize, right: usize) -> usize { left + right }
|
||||||
|
|
||||||
struct Params {
|
struct Context {
|
||||||
w: usize
|
lgw: u32,
|
||||||
|
w: usize,
|
||||||
|
len1: usize
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! functionality {
|
macro_rules! functionality {
|
||||||
() => {
|
() => {
|
||||||
use zeroize::{Zeroize, ZeroizeOnDrop};
|
|
||||||
use crate::traits::PK;
|
use crate::traits::PK;
|
||||||
|
use crate::Context;
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
// ----- 'EXTERNAL' DATA TYPES -----
|
// ----- 'EXTERNAL' DATA TYPES -----
|
||||||
|
|
||||||
|
static CONTEXT: Context = Context{lgw: LGW, w: 2_usize.pow(LGW), len1: (8*N).div_ceil(LGW as usize)};
|
||||||
|
|
||||||
/// Correctly sized private key specific to the target security parameter set. <br>
|
/// Correctly sized private key specific to the target security parameter set. <br>
|
||||||
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
#[derive(Clone, Zeroize, ZeroizeOnDrop)]
|
||||||
pub struct PrivateKey {
|
pub struct PrivateKey {
|
||||||
|
|
@ -36,9 +42,9 @@ macro_rules! functionality {
|
||||||
|
|
||||||
impl PK for PrivateKey {
|
impl PK for PrivateKey {
|
||||||
type Seed = [u8; N];
|
type Seed = [u8; N];
|
||||||
fn seed(&self) -> [u8; N] {self.sk_seed}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
fn seed(&self) -> [u8; N] { self.sk_seed }
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,4 @@
|
||||||
|
|
||||||
|
|
||||||
pub trait PK {
|
pub trait PK {
|
||||||
type Seed;
|
type Seed;
|
||||||
fn seed(&self) -> Self::Seed;
|
fn seed(&self) -> Self::Seed;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
48
src/types.rs
Normal file
48
src/types.rs
Normal file
|
|
@ -0,0 +1,48 @@
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use zeroize::{Zeroize, ZeroizeOnDrop};
|
||||||
|
|
||||||
|
const WOTS_HASH: u32 = 0;
|
||||||
|
pub(crate) const WOTS_PK: u32 = 1;
|
||||||
|
const TREE: u32 = 2;
|
||||||
|
const FORS_TREE: u32 = 3;
|
||||||
|
const FORS_ROOTS: u32 = 4;
|
||||||
|
pub(crate) const WOTS_PRF: u32 = 5;
|
||||||
|
const FORS_PRF: u32 = 6;
|
||||||
|
|
||||||
|
|
||||||
|
/// Straddling the line between struct, enum and union...
|
||||||
|
#[derive(Clone, Default, Zeroize, ZeroizeOnDrop)]
|
||||||
|
#[repr(align(32))]
|
||||||
|
pub struct ADRS {
|
||||||
|
f0: [u8; 4], // layer address
|
||||||
|
f1: [u8; 4], // tree address (LSB?)
|
||||||
|
f2: [u8; 4], // tree address
|
||||||
|
f3: [u8; 4], // tree address (MSB)
|
||||||
|
f4: [u8; 4], // type
|
||||||
|
f5: [u8; 4], // key pair address OR padding
|
||||||
|
f6: [u8; 4], // chain address OR padding OR tree height
|
||||||
|
f7: [u8; 4], // hash address OR padding ORtree index OR hash address = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl ADRS {
|
||||||
|
pub(crate) fn get_key_pair_address(&self) -> [u8; 4] { self.f5 }
|
||||||
|
|
||||||
|
pub(crate) fn set_key_pair_address(&mut self, kp_addr: [u8; 4]) { self.f5 = kp_addr; }
|
||||||
|
|
||||||
|
pub(crate) fn set_chain_address(&mut self, i: usize) { self.f6 = (i as u32).to_be_bytes(); }
|
||||||
|
|
||||||
|
pub(crate) fn set_type_and_clear(&mut self, type_t: u32) {
|
||||||
|
self.f4 = type_t.to_be_bytes();
|
||||||
|
self.f5 = 0u32.to_be_bytes();
|
||||||
|
self.f6 = 0u32.to_be_bytes();
|
||||||
|
self.f7 = 0u32.to_be_bytes();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn set_hash_address(&mut self, addr: u32) { self.f7 = addr.to_be_bytes(); }
|
||||||
|
|
||||||
|
pub(crate) fn to_bytes(&self) -> Vec<u8> {
|
||||||
|
let x = [self.f0, self.f1, self.f2, self.f3].concat();
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue