diff --git a/Cargo.toml b/Cargo.toml index 139fdae..dfad9b2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,11 +1,31 @@ [package] -name = "slh-dsa-rs" +name = "fips205" version = "0.1.1" edition = "2021" license = "MIT OR Apache-2.0" description = "FIPS 205 (draft): Stateless Hash-Based Digital Signature Standard" -repository = "https://github.com/integritychain/slh-dsa-rs" +repository = "https://github.com/integritychain/fips205" rust-version = "1.73" [dependencies] +zeroize = { version = "1.6.0", features = ["zeroize_derive"] } +rand_core = { version = "0.6.4", default-features = false } + +[features] +default = ["default-rng", "slh_dsa_sha2_128s", "slh_dsa_shake_128s", "slh_dsa_sha2_128f", "slh_dsa_shake_128f", + "slh_dsa_sha2_192s", "slh_dsa_shake_192s", "slh_dsa_sha2_192f", "slh_dsa_shake_192f", + "slh_dsa_sha2_256s", "slh_dsa_shake_256s", "slh_dsa_sha2_256f", "slh_dsa_shake_256f"] +default-rng = ["rand_core/getrandom"] +slh_dsa_sha2_128s = [] +slh_dsa_shake_128s = [] +slh_dsa_sha2_128f = [] +slh_dsa_shake_128f = [] +slh_dsa_sha2_192s = [] +slh_dsa_shake_192s = [] +slh_dsa_sha2_192f = [] +slh_dsa_shake_192f = [] +slh_dsa_sha2_256s = [] +slh_dsa_shake_256s = [] +slh_dsa_sha2_256f = [] +slh_dsa_shake_256f = [] diff --git a/src/algs.rs b/src/algs.rs new file mode 100644 index 0000000..c1e1089 --- /dev/null +++ b/src/algs.rs @@ -0,0 +1,502 @@ +use alloc::vec; +use alloc::vec::Vec; +use crate::Params; +use crate::traits::PK; + +/// Algorithm 1: `toInt(X, n)` on page 14. +/// Convert a byte string to an integer. +/// +/// Input: n-byte string `X`.
+/// Output: Integer value of `X`. +pub(crate) fn to_int(x: &[u8], n: usize) -> u64 { + assert_eq!(x.len(), n); + // 1: total ← 0 + let mut total = 0_u64; + // 2: + // 3: for i from 0 to n − 1 do + for i in 0..n { + // 4: total ← 256 · total + X[i] + total = (total << 8) + x[i] as u64; + // 5: end for + } + // 6: return total + total +} + + +/// Algorithm 2: `toByte(x, n)` on page 15. +/// Convert an integer to a byte string. +/// +/// Input: Integer `x`, string length `n`.
+/// Output: Byte string of length `n` containing binary representation of `x` in big-endian byte-order. +pub(crate) fn to_byte(x: u64, n: usize) -> Vec { + let mut s = vec![0u8; n]; + + // 1: total ← x + let mut total = x; + + // 2: + + // 3: for i from 0 to n − 1 do + for i in 0..n { + // + // 4: S[n − 1 − i] ← total mod 256 ▷ Least significant 8 bits of total + s[n - 1 - i] = total as u8; + + // 5: total ← total ≫ 8 + total >>= 8; + + // 6: end for + } + + // 7: return S + s +} + + +/// Algorithm 3: `base_2^b(X, b, out_len)` on page 15. +/// Compute the base 2^b representation of X. +/// +/// Input: Byte string `X` of length at least ceil(`out_len·b/8`), integer `b`, output length `out_len`.
+/// Output: Array of `out_len` integers in the range `[0, . . . , 2^b − 1]`. +pub(crate) fn base_2b(x: &[u8], b: u32, out_len: usize) -> Vec { + assert!(x.len() >= out_len * b as usize / 8); + let mut baseb = vec![0u64; out_len]; + // 1: in ← 0 + let mut inn = 0; + // 2: bits ← 0 + let mut bits = 0; + // 3: total ← 0 + let mut total = 0; + // 4: + // 5: for out from 0 to out_len − 1 do + for out in 0..out_len { + // 6: while bits < b do + while bits < b { + // 7: total ← (total ≪ 8) + X[in] + total = (total << 8) + x[inn] as u64; + // 8: in ← in + 1 + inn += 1; + // 9: bits ← bits + 8 + bits += 8; + // 10: end while + } + // 11: bits ← bits − b + bits -= b; + // 12: baseb[out] ← (total ≫ bits) mod 2^b + baseb[out] = (total >> bits) & (2u64.pow(b) - 1); + // 13: end for + } + // 14: return baseb + baseb +} + + +/// Algorithm 4: `chain(X, i, s, PK.seed, ADRS)` on page 17. +/// Chaining function used in WOTS+. +/// +/// Input: Input string `X`, start index `i`, number of steps `s`, public seed `PK.seed`, address `ADRS`.
+/// 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]> { + // 1: if (i + s) ≥ w then + if (i + s) >= params.w { + // 2: return NULL + return None; + // 3: end if + } + // 4: + // 5: tmp ← X + let mut tmp = [0u8; 32]; // Check digest width + // 6: + // 7: for j from i to i + s − 1 do + for j in i..(i+s) { + // 8: ADRS.setHashAddress(j) + + // 9: tmp ← F(PK.seed, ADRS, tmp) + + // 10: end for + } + // 11: return tmp + Some(tmp) +} + + +/// Algorithm 5: `wots_PKgen(SK.seed, PK.seed, ADRS)` on page 18. +/// Generate a WOTS+ public key. +/// +/// Input: Secret seed `SK.seed`, public seed `PK.seed`, address `ADRS`.
+/// Output: WOTS+ public key `pk`. +const _A5: u32 = 0; +// 1: skADRS ← ADRS ▷ Copy address to create key generation key address +// 2: skADRS.setTypeAndClear(WOTS_PRF) +// 3: skADRS.setKeyPairAddress(ADRS.getKeyPairAddress()) +// 4: for i from 0 to len − 1 do +// 5: skADRS.setChainAddress(i) +// 6: sk ← PRF(PK.seed, SK.seed, skADRS) ▷ Compute secret value for chain i +// 7: ADRS.setChainAddress(i) +// 8: tmp[i] ← chain(sk, 0, w − 1, PK.seed, ADRS) ▷ Compute public value for chain i +// 9: end for +// 10: wotspkADRS ← ADRS ▷ Copy address to create WOTS+ public key address +// 11: wotspkADRS.setTypeAndClear(WOTS_PK) +// 12: wotspkADRS.setKeyPairAddress(ADRS.getKeyPairAddress()) +// 13: pk ← Tlen (PK.seed, wotspkADRS,tmp) ▷ Compress public key +// 14: return pk + + +/// Algorithm 6: `wots_sign(M, SK.seed, PK.seed, ADRS)` on page 19. +/// Generate a WOTS+ signature on an n-byte message. +/// +/// Input: Message `M`, secret seed `SK.seed`, public seed `PK.seed`, address `ADRS`.
+/// Output: WOTS+ signature sig. +const _A6: u32 = 0; +// 1: csum ← 0 +// 2: +// 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 + + +/// Algorithm 7: `wots_PKFromSig(sig, M, PK.seed, ADRS)` on page 20. +/// Compute a WOTS+ public key from a message and its signature. +/// +/// Input: WOTS+ signature `sig`, message `M`, public seed `PK.seed`, address `ADRS`.
+/// Output: WOTS+ public key `pksig` derived from `sig`. +const _A7: u32 = 0; +// 1: csum ← 0 +// 2: +// 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: for i from 0 to len − 1 do +// 12: ADRS.setChainAddress(i) +// 13: tmp[i] ← chain(sig[i], msg[i], w − 1 − msg[i], PK.seed, ADRS) +// 14: end for +// 15: wotspkADRS ← ADRS +// 16: wotspkADRS.setTypeAndClear(WOTS_PK) +// 17: wotspkADRS.setKeyPairAddress(ADRS.getKeyPairAddress()) +// 18: pksig ← Tlen (PK.seed, wotspkADRS, tmp) +// 19: return pksig + + +/// Algorithm 8: `xmss_node(SK.seed, i, z, PK.seed, ADRS)` on page 22. +/// Compute the root of a Merkle subtree of WOTS+ public keys. +/// +/// Input: Secret seed `SK.seed`, target node index `i`, target node height `z`, public seed `PK.seed`, +/// `address ADRS`.
+/// Output: n-byte root `node`. +const _A8: u32 = 0; +// 1: if z > h′ or i ≥ 2^{h −z} then +// 2: return NULL +// 3: end if +// 4: if z = 0 then +// 5: ADRS.setTypeAndClear(WOTS_HASH) +// 6: ADRS.setKeyPairAddress(i) +// 7: node ← wots_PKgen(SK.seed, PK.seed, ADRS) +// 8: else +// 9: lnode ← xmss_node(SK.seed, 2i, z − 1, PK.seed, ADRS) +// 10: rnode ← xmss_node(SK.seed, 2i + 1, z − 1, PK.seed, ADRS) +// 11: ADRS.setTypeAndClear(TREE) +// 12: ADRS.setTreeHeight(z) +// 13: ADRS.setTreeIndex(i) +// 14: node ← H(PK.seed, ADRS, lnode ∥ rnode) +// 15: end if +// 16: return node + + +/// Algorithm 9: `xmss_sign(M, SK.seed, idx, PK.seed, ADRS)` on page 23. +/// Generate an XMSS signature. +/// +/// Input: n-byte message `M`, secret seed `SK.seed`, index `idx`, public seed `PK.seed`, address `ADRS`.
+/// Output: XMSS signature SIGXMSS = (sig ∥ AUTH). +const _A9: u32 = 0; +// +// 1: for j from 0 to h′-1 do ▷ Build authentication path +// 2: k ← idx/2 xor 1 +// 3: AUTH[j] ← xmss_node(SK.seed, k, j, PK.seed, ADRS) +// 4: end for +// 5: +// 6: ADRS.setTypeAndClear(WOTS_HASH) +// 7: ADRS.setKeyPairAddress(idx) +// 8: sig ← wots_sign(M, SK.seed, PK.seed, ADRS) +// 9: SIG_XMSS ← sig ∥ AUTH +// 10: return SIG_XMSS + + +/// Algorithm 10: `xmss_PKFromSig(idx, SIG_XMSS, M, PK.seed, ADRS)` +/// Compute an XMSS public key from an XMSS signature. +/// +/// Input: Index `idx`, XMSS signature `SIG_XMSS = (sig ∥ AUTH)`, n-byte message `M`, public seed `PK.seed`, +/// address `ADRS`.
+/// Output: n-byte root value `node[0]`. +const _A10: u32 = 0; +// 1: ADRS.setTypeAndClear(WOTS_HASH) ▷ Compute WOTS+ pk from WOTS+ sig +// 2: ADRS.setKeyPairAddress(idx) +// 3: sig ← SIG_XMSS .getWOTSSig() ▷ SIG_XMSS [0 : len · n] +// 4: AUTH ← SIG_XMSS .getXMSSAUTH() ▷ SIG_XMSS [len · n : (len + h′) · n] +// 5: node[0] ← wots_PKFromSig(sig, M, PK.seed, ADRS) +// 6: +// 7: ADRS.setTypeAndClear(TREE) ▷ Compute root from WOTS+ pk and AUTH +// 8: ADRS.setTreeIndex(idx) +// 9: for k from 0 to h′ − 1 do +// 10: ADRS.setTreeHeight(k + 1) +// 11: if idx/2^k is even then +// 12: ADRS.setTreeIndex(ADRS.getTreeIndex()/2) +// 13: node[1] ← H(PK.seed, ADRS, node[0] ∥ AUTH[k]) +// 14: else +// 15: ADRS.setTreeIndex((ADRS.getTreeIndex() − 1)/2) +// 16: node[1] ← H(PK.seed, ADRS, AUTH[k] ∥ node[0]) +// 17: end if +// 18: node[0] ← node[1] +// 19: end for +// 20: return node[0] + + +/// Algorithm 11: `ht_sign(M, SK.seed, PK.seed, idx_tree, idx_leaf)` on page 27. +/// Generate a hypertree signature. +/// +/// Input: Message `M`, private seed `SK.seed`, public seed `PK.seed`, tree index `idx_tree`, leaf +/// index `idx_leaf`.
+/// Output: HT signature SIG_HT. +const _A11: u32 = 0; +// 1: ADRS ← toByte(0, 32) +// 2: +// 3: ADRS.setTreeAddress(idxtree) +// 4: SIG_tmp ← xmss_sign(M, SK.seed, idxleaf, PK.seed, ADRS) +// 5: SIG_HT ← SIG_tmp +// 6: root ← xmss_PKFromSig(idx_leaf, SIG_tmp, M, PK.seed, ADRS) +// 7: for j from 1 to d − 1 do +// 8: idx_leaf ← idx_tree mod 2^{h′} ▷ h′ least significant bits of idx_tree +// 9: idx_tree ← idx_tree ≫ h′ ▷ Remove least significant h′ bits from idx_tree +// 10: ADRS.setLayerAddress(j) +// 11: ADRS.setTreeAddress(idx_tree) +// 12: SIG_tmp ← xmss_sign(root, SK.seed, idx_leaf, PK.seed, ADRS) +// 13: SIG_HT ← SIG_HT ∥ SIG_tmp +// 14: if j < d − 1 then +// 15: root ← xmss_PKFromSig(idx_leaf, SIG_tmp, root, PK.seed, ADRS) +// 16: end if +// 17: end for +// 18: return SIGHT + + +/// Algorithm 12: `ht_verify(M, SIG_HT, PK.seed, idx_tree, idx_leaf, PK.root)` on page 28. +/// Verify a hypertree signature. +/// +/// Input: Message `M`, signature `SIG_HT`, public seed `PK.seed`, tree index `idx_tree`, leaf index `idx_leaf`, +/// HT public key `PK.root`.
+/// Output: Boolean. +const _A12: u32 = 0; +// 1: ADRS ← toByte(0, 32) +// 2: +// 3: ADRS.setTreeAddress(idx_tree) +// 4: SIG_tmp ← SIG_HT.getXMSSSignature(0) ▷ SIG_HT [0 : (h′ + len) · n] +// 5: node ← xmss_PKFromSig(idx_leaf, SIG_tmp, M, PK.seed, ADRS) +// 6: for j from 1 to d − 1 do +// 7: idx_leaf ← idx_tree mod 2^{h′} ▷ h′ least significant bits of idx_tree +// 8: idx_tree ← idx_tree ≫ h′ ▷ Remove least significant h′ bits from idx_tree +// 9: ADRS.setLayerAddress(j) +// 10: ADRS.setTreeAddress(idx_tree) +// 11: SIG_tmp ← SIG_HT.getXMSSSignature(j) ▷ SIGHT [ j · (h′ + len) · n : ( j + 1)(h′ + len) · n] +// 12: node ← xmss_PKFromSig(idx_leaf, SIG_tmp, node, PK.seed, ADRS) +// 13: end for +// 14: if node = PK.root then +// 15: return true +// 16: else +// 17: return false +// 18: end if + + +/// Algorithm 13: `fors_SKgen(SK.seed, PK.seed, ADRS, idx)` on page 29. +/// Generate a FORS private-key value. +/// +/// Input: Secret seed `SK.seed`, public seed `PK.seed`, address `ADRS`, secret key index `idx`.
+/// Output: n-byte FORS private-key value. +const _A13: u32 = 0; +// 1: skADRS ← ADRS ▷ Copy address to create key generation address +// 2: skADRS.setTypeAndClear(FORS_PRF) +// 3: skADRS.setKeyPairAddress(ADRS.getKeyPairAddress()) +// 4: skADRS.setTreeIndex(idx) +// 5: return PRF(PK.seed, SK.seed, skADRS) + + +/// Algorithm 14: `fors_node(SK.seed, i, z, PK.seed, ADRS)` on page 30. +/// Compute the root of a Merkle subtree of FORS public values. +/// +/// Input: Secret seed `SK.seed`, target node index `i`, target node height `z`, public seed `PK.seed`, +/// address `ADRS`.
+/// Output: n-byte root node. +const _A14: u32 = 0; +// 1: if z > a or i ≥ k · 2(a−z) then +// 2: return NULL +// 3: end if +// 4: if z = 0 then +// 5: sk ← fors_SKgen(SK.seed, PK.seed, ADRS, i) +// 6: ADRS.setTreeHeight(0) +// 7: ADRS.setTreeIndex(i) +// 8: node ← F(PK.seed, ADRS, sk) +// 9: else +// 10: lnode ← fors_node(SK.seed, 2i, z − 1, PK.seed, ADRS) +// 11: rnode ← fors_node(SK.seed, 2i + 1, z − 1, PK.seed, ADRS) +// 12: ADRS.setTreeHeight(z) +// 13: ADRS.setTreeIndex(i) +// 14: node ← H(PK.seed, ADRS, lnode ∥ rnode) +// 15: end if +// 16: return node + + +/// Algorithm 15: `fors_sign(md, SK.seed, PK.seed, ADRS)` +/// Generate a FORS signature. +/// +/// Input: Message digest `md`, secret seed `SK.seed`, address `ADRS`, public seed `PK.seed`.
+/// Output: FORS signature `SIG_FORS`. +const _A15: u32 = 0; +// 1: SIG_FORS = NULL ▷ Initialize SIG_FORS as a zero-length byte string +// 2: indices ← base_2^b(md, a, k) +// 3: for i from 0 to k − 1 do ▷ Compute signature elements +// 4: SIG_FORS ← SIG_FORS ∥ fors_SKgen(SK.seed, PK.seed, ADRS, i · 2a + indices[i]) +// 5: +// 6: for j from 0 to a − 1 do ▷ Compute auth path +// 7: s ← indices[i]/2^j xor 1 +// 8: AUTH[j] ← fors_node(SK.seed, i · 2^{a−j} + s, j, PK.seed, ADRS) +// 9: end for +// 10: SIG_FORS ← SIG_FORS ∥ AUTH +// 11: end for +// 12: return SIG_FORS + + +/// Algorithm 16: `fors_pkFromSig(SIG_FORS, md, PK.seed, ADRS)` on page 32. +/// Compute a FORS public key from a FORS signature. +/// +/// Input: FORS signature `SIG_FORS`, message digest `md`, public seed `PK.seed`, address `ADRS`.
+/// Output: FORS public key. +const _A16: u32 = 0; +// 1: indices ← base_2^b(md, a, k) +// 2: for i from 0 to k − 1 do +// 3: sk ← SIG_FORS.getSK(i) ▷ SIG_FORS [i · (a + 1) · n : (i · (a + 1) + 1) · n] +// 4: ADRS.setTreeHeight(0) ▷ Compute leaf +// 5: ADRS.setTreeIndex(i · 2^a + indices[i]) +// 6: node[0] ← F(PK.seed, ADRS, sk) +// 7: +// 8: auth ← SIGFORS .getAUTH(i) ▷ SIGFORS [(i · (a + 1) + 1) · n : (i + 1) · (a + 1) · n] +// 9: for j from 0 to a − 1 do ▷ Compute root from leaf and AUTH +// 10: ADRS.setTreeHeight(j + 1) +// 11: if indices[i]/2^jj is even then +// 12: ADRS.setTreeIndex(ADRS.getTreeIndex()/2) +// 13: node[1] ← H(PK.seed, ADRS, node[0] ∥ auth[j]) +// 14: else +// 15: ADRS.setTreeIndex((ADRS.getTreeIndex() − 1)/2) +// 16: node[1] ← H(PK.seed, ADRS, auth[j] ∥ node[0]) +// 17: end if +// 18: node[0] ← node[1] +// 19: end for +// 20: root[i] ← node[0] +// 21: end for +// 22: forspkADRS ← ADRS ▷ Compute the FORS public key from the Merkle tree roots +// 23: forspkADRS.setTypeAndClear(FORS_ROOTS) +// 24: forspkADRS.setKeyPairAddress(ADRS.getKeyPairAddress()) +// 25: pk ← Tk(PK.seed, forspkADRS, root) +// 26: return pk; + + +/// Algorithm 17: `slh_keygen()` on page 34. +/// Generate an SLH-DSA key pair. +/// +/// Input: (none)
+/// Output: SLH-DSA key pair `(SK, PK)`. +const _A17: u32 = 0; +// 1: SK.seed ←$ B^n ▷ Set SK.seed, SK.prf, and PK.seed to random n-byte +// 2: SK.prf ←$ B^n ▷ strings using an approved random bit generator +// 3: PK.seed ←$ B^n +// 4: +// 5: ADRS ← toByte(0, 32) ▷ Generate the public key for the top-level XMSS tree +// 6: ADRS.setLayerAddress(d − 1) +// 7: PK.root ← xmss_node(SK.seed, 0, h′, PK.seed, ADRS) +// 8: +// 9: return ( (SK.seed, SK.prf, PK.seed, PK.root), (PK.seed, PK.root) ) + + +/// Algorithm 18: `slh_sign(M, SK)` on page 35. +/// Generate an SLH-DSA signature. +/// +/// Input: Message `M`, private key `SK = (SK.seed, SK.prf, PK.seed, PK.root)`.
+/// Output: SLH-DSA signature `SIG`. +const _A18: u32 = 0; +// 1: ADRS ← toByte(0, 32) +// 2: +// 3: opt_rand ← PK.seed ▷ Set opt_rand to either PK.seed +// 4: if (RANDOMIZE) then ▷ or to a random n-byte string +// 5: opt_rand ←$ Bn +// 6: end if +// 7: R ← PRF_msg(SK.prf, opt_rand, M) ▷ Generate randomizer +// 8: SIG ← R +// 9: +// 10: digest ← H_msg(R, PK.seed, PK.root, M) ▷ Compute message digest +// 11: md ← digest[0 : ceil(k·a/8)] ▷ first ceil(k·a/8) bytes +// 12: tmp_idx_tree ← digest[ceil(k·a/8) : ceil(k·a/8) + ceil((h-h/d)/8)] ▷ next ceil((h-h/d)/8) bytes +// 13: tmp_idx_leaf ← digest[ceil(k·a/8) + ceil((h-h/d)/8) : ceil(k·a/8) + ceil((h-h/d)/8) + ceil(h/8d)] ▷ next ceil(h/8d) bytes +// 14: +// 15: idx_tree ← toInt(tmp_idx_tree, ceil((h-h/d)/8)) mod 2^{h−h/d} +// 16: idx_leaf ← toInt(tmp_idx_leaf, ceil(h/8d) mod 2^{h/d} +// 17: +// 18: ADRS.setTreeAddress(idx_tree) +// 19: ADRS.setTypeAndClear(FORS_TREE) +// 20: ADRS.setKeyPairAddress(idxleaf) +// 21: SIG_FORS ← fors_sign(md, SK.seed, PK.seed, ADRS) +// 22: SIG ← SIG ∥ SIG_FORS +// 23: +// 24: PK_FORS ← fors_pkFromSig(SIG_FORS , md, PK.seed, ADRS) ▷ Get FORS key +// 25: +// 26: SIG_HT ← ht_sign(PK_FORS , SK.seed, PK.seed, idx_tree, idx_leaf) +// 27: SIG ← SIG ∥ SIG_HT +// 28: return SIG + + +/// Algorithm 19: `slh_verify(M, SIG, PK)` +/// Verify an SLH-DSA signature. +/// +/// Input: Message `M`, signature `SIG`, public key `PK = (PK.seed, PK.root)`.
+/// Output: Boolean. +const _A19: u32 = 0; +// 1: if |SIG| != (1 + k(1 + a) + h + d · len) · n then +// 2: return false +// 3: end if +// 4: ADRS ← toByte(0, 32) +// 5: R ← SIG.getR() ▷ SIG[0 : n] +// 6: SIG_FORS ← SIG.getSIG_FORS() ▷ SIG[n : (1 + k(1 + a)) · n] +// 7: SIG_HT ← SIG.getSIG_HT() ▷ SIG[(1 + k(1 + a)) · n : (1 + k(1 + a) + h + d · len) · n] +// 8: +// 9: digest ← Hmsg(R, PK.seed, PK.root, M) ▷ Compute message digest +// 10: md ← digest[0 : ceil(k·a/8)] ▷ first ceil(k·a/8) bytes +// 11: tmp_idx_tree ← digest[ceil(k·a/8) : ceil(k·a/8) + ceil((h - h/d)/8)] ▷ next ceil((h - h/d)/8) bytes +// 12: tmp_idx_leaf ← digest[ceil(k·a/8) + ceil((h - h/d)/8) : ceil(k·a/8) + ceil((h - h/d)/8) + ceil(h/8d)] ▷ next ceil(h/8d) bytes +// 13: +// 14: idx_tree ← toInt(tmp_idx_tree, ceil((h - h/d)/8)) mod 2^{h−h/d} +// 15: idx_leaf ← toInt(tmp_idx_leaf, ceil(h/8d) mod 2^{h/d} +// 16: +// 17: ADRS.setTreeAddress(idx_tree) ▷ Compute FORS public key +// 18: ADRS.setTypeAndClear(FORS_TREE) +// 19: ADRS.setKeyPairAddress(idx_leaf) +// 20: +// 21: PK_FORS ← fors_pkFromSig(SIG_FORS, md, PK.seed, ADRS) +// 22: +// 23: return ht_verify(PK_FORS, SIG_HT, PK.seed, idx_tree , idx_leaf, PK.root) diff --git a/src/lib.rs b/src/lib.rs index 7d12d9a..75b4dea 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,269 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right +#![no_std] +#![deny(clippy::pedantic)] +#![deny(warnings)] +#![deny(missing_docs)] +#![allow(dead_code)] + +//! TKTK crate doc + +extern crate alloc; + +mod algs; +mod traits; + +/// to be deleted +#[must_use] +pub fn add(left: usize, right: usize) -> usize { left + right } + +struct Params { + w: usize } +macro_rules! functionality { + () => { + use zeroize::{Zeroize, ZeroizeOnDrop}; + use crate::traits::PK; + // ----- 'EXTERNAL' DATA TYPES ----- + + /// Correctly sized private key specific to the target security parameter set.
+ #[derive(Clone, Zeroize, ZeroizeOnDrop)] + pub struct PrivateKey { + pub(crate) sk_seed: [u8; N], + sk_prf: [u8; N], + pk_seed: [u8; N], + pk_root: [u8; N], + } + + impl PK for PrivateKey { + type Seed = [u8; N]; + fn seed(&self) -> [u8; N] {self.sk_seed} + } + + }; +} + +/// TKTK +#[cfg(feature = "slh_dsa_sha2_128s")] +pub mod slh_dsa_sha2_128s { + const N: usize = 16; + const H: u32 = 63; + const D: u32 = 7; + const H_PRIME: u32 = 9; + const A: u32 = 12; + const K: u32 = 14; + const LGW: u32 = 4; + const M: u32 = 30; + const PK_LEN: usize = 32; + const SIG_LEN: usize = 7856; + const SK_LEN: usize = 0000; + + functionality!(); +} + + +/// TKTK +#[cfg(feature = "slh_dsa_shake_128s")] +pub mod slh_dsa_shake_128s { + const N: usize = 16; + const H: u32 = 63; + const D: u32 = 7; + const H_PRIME: u32 = 9; + const A: u32 = 12; + const K: u32 = 14; + const LGW: u32 = 4; + const M: u32 = 30; + const PK_LEN: usize = 32; + const SIG_LEN: usize = 7856; + const SK_LEN: usize = 0000; + + functionality!(); +} + +/// TKTK +#[cfg(feature = "slh_dsa_sha2_128f")] +pub mod slh_dsa_sha2_128f { + const N: usize = 16; + const H: u32 = 66; + const D: u32 = 22; + const H_PRIME: u32 = 3; + const A: u32 = 6; + const K: u32 = 33; + const LGW: u32 = 4; + const M: u32 = 34; + const PK_LEN: usize = 32; + const SIG_LEN: usize = 17088; + const SK_LEN: usize = 0000; + + functionality!(); +} + +/// TKTK +#[cfg(feature = "slh_dsa_shake_128f")] +pub mod slh_dsa_shake_128f { + const N: usize = 16; + const H: u32 = 66; + const D: u32 = 22; + const H_PRIME: u32 = 3; + const A: u32 = 6; + const K: u32 = 33; + const LGW: u32 = 4; + const M: u32 = 34; + const PK_LEN: usize = 32; + const SIG_LEN: usize = 17088; + const SK_LEN: usize = 0000; + + functionality!(); +} + + +/// TKTK +#[cfg(feature = "slh_dsa_sha2_192s")] +pub mod slh_dsa_sha2_192s { + const N: usize = 24; + const H: u32 = 63; + const D: u32 = 7; + const H_PRIME: u32 = 9; + const A: u32 = 14; + const K: u32 = 17; + const LGW: u32 = 4; + const M: u32 = 39; + const PK_LEN: usize = 48; + const SIG_LEN: usize = 16224; + const SK_LEN: usize = 00000; + + functionality!(); +} + + +/// TKTK +#[cfg(feature = "slh_dsa_shake_192s")] +pub mod slh_dsa_shake_192s { + const N: usize = 24; + const H: u32 = 63; + const D: u32 = 7; + const H_PRIME: u32 = 9; + const A: u32 = 14; + const K: u32 = 17; + const LGW: u32 = 4; + const M: u32 = 39; + const PK_LEN: usize = 48; + const SIG_LEN: usize = 16224; + const SK_LEN: usize = 00000; + + functionality!(); +} + +/// TKTK +#[cfg(feature = "slh_dsa_sha2_192f")] +pub mod slh_dsa_sha2_192f { + const N: usize = 24; + const H: u32 = 66; + const D: u32 = 22; + const H_PRIME: u32 = 3; + const A: u32 = 8; + const K: u32 = 33; + const LGW: u32 = 4; + const M: u32 = 42; + const PK_LEN: usize = 48; + const SIG_LEN: usize = 35664; + const SK_LEN: usize = 0000; + + functionality!(); +} + +/// TKTK +#[cfg(feature = "slh_dsa_shake_192f")] +pub mod slh_dsa_shake_192f { + const N: usize = 24; + const H: u32 = 66; + const D: u32 = 22; + const H_PRIME: u32 = 3; + const A: u32 = 8; + const K: u32 = 33; + const LGW: u32 = 4; + const M: u32 = 42; + const PK_LEN: usize = 48; + const SIG_LEN: usize = 35664; + const SK_LEN: usize = 0000; + + functionality!(); +} + + +/// TKTK +#[cfg(feature = "slh_dsa_sha2_256s")] +pub mod slh_dsa_sha2_256s { + const N: usize = 32; + const H: u32 = 64; + const D: u32 = 8; + const H_PRIME: u32 = 8; + const A: u32 = 14; + const K: u32 = 22; + const LGW: u32 = 4; + const M: u32 = 47; + const PK_LEN: usize = 64; + const SIG_LEN: usize = 29792; + const SK_LEN: usize = 0000; + + functionality!(); +} + + +/// TKTK +#[cfg(feature = "slh_dsa_shake_256s")] +pub mod slh_dsa_shake_256s { + const N: usize = 32; + const H: u32 = 64; + const D: u32 = 8; + const H_PRIME: u32 = 8; + const A: u32 = 14; + const K: u32 = 22; + const LGW: u32 = 4; + const M: u32 = 47; + const PK_LEN: usize = 64; + const SIG_LEN: usize = 29792; + const SK_LEN: usize = 0000; + + functionality!(); +} + +/// TKTK +#[cfg(feature = "slh_dsa_sha2_256f")] +pub mod slh_dsa_sha2_256f { + const N: usize = 32; + const H: u32 = 68; + const D: u32 = 17; + const H_PRIME: u32 = 4; + const A: u32 = 9; + const K: u32 = 35; + const LGW: u32 = 4; + const M: u32 = 49; + const PK_LEN: usize = 64; + const SIG_LEN: usize = 49856; + const SK_LEN: usize = 0000; + + functionality!(); +} + +/// TKTK +#[cfg(feature = "slh_dsa_shake_256f")] +pub mod slh_dsa_shake_256f { + const N: usize = 32; + const H: u32 = 68; + const D: u32 = 17; + const H_PRIME: u32 = 4; + const A: u32 = 9; + const K: u32 = 35; + const LGW: u32 = 4; + const M: u32 = 49; + const PK_LEN: usize = 64; + const SIG_LEN: usize = 49856; + const SK_LEN: usize = 0000; + + functionality!(); +} + + #[cfg(test)] mod tests { use super::*; diff --git a/src/traits.rs b/src/traits.rs new file mode 100644 index 0000000..792475b --- /dev/null +++ b/src/traits.rs @@ -0,0 +1,6 @@ + + +pub trait PK { + type Seed; + fn seed(&self) -> Self::Seed; +} \ No newline at end of file