commit 955b8c396c58d99af9a5eeab11ccee3ec8702578 Author: h Date: Wed May 8 15:26:44 2024 -0400 push online diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..ea8c4bf --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +/target diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..f18ab6d --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,290 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "autocfg" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" + +[[package]] +name = "cfg-if" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" + +[[package]] +name = "crossbeam-deque" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef" +dependencies = [ + "cfg-if", + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae211234986c545741a7dc064309f67ee1e5ad243d0e48335adc0484d960bcc7" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a22b2d63d4d1dc0b7f1b6b2747dd0088008a9be28b6ddf0b1e7d335e3037294" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crunchy" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" + +[[package]] +name = "either" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" + +[[package]] +name = "fuchsia-cprng" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06f77d526c1a601b7c4cdd98f54b5eaabffc14d5f2f0296febdc7f357c6d3ba" + +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + +[[package]] +name = "getrandom" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe9006bed769170c11f845cf00c7c1e9092aeb3f268e007c3e760ac68008070f" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.11.0+wasi-snapshot-preview1", +] + +[[package]] +name = "libc" +version = "0.2.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" + +[[package]] +name = "mayo" +version = "0.1.0" +dependencies = [ + "rand 0.8.5", + "rayon", + "rust-crypto", + "tiny-keccak", +] + +[[package]] +name = "memoffset" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a634b1c61a95585bd15607c6ab0c4e5b226e695ff2800ba0cdccddf208c406c" +dependencies = [ + "autocfg", +] + +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ac302d8f83c0c1974bf758f6b041c6c8ada916fbb44a609158ca8b064cc76c" +dependencies = [ + "libc", + "rand 0.4.6", +] + +[[package]] +name = "rand" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "552840b97013b1a26992c11eac34bdd778e464601a4c2054b5f0bff7c6761293" +dependencies = [ + "fuchsia-cprng", + "libc", + "rand_core 0.3.1", + "rdrand", + "winapi", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a6fdeb83b075e8266dcc8762c22776f6877a63111121f5f8c7411e5be7eed4b" +dependencies = [ + "rand_core 0.4.2", +] + +[[package]] +name = "rand_core" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c33a3c44ca05fa6f1807d8e6743f3824e8509beca625669633be0acbdf509dc" + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rayon" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c27db03db7734835b3f53954b534c91069375ce6ccaa2e065441e07d9b6cdb1" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ce3fb6ad83f861aac485e76e1985cd109d9a3713802152be56c3b1f0e0658ed" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[package]] +name = "rdrand" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "678054eb77286b51581ba43620cc911abf02758c91f93f479767aed0f90458b2" +dependencies = [ + "rand_core 0.3.1", +] + +[[package]] +name = "rust-crypto" +version = "0.2.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f76d05d3993fd5f4af9434e8e436db163a12a9d40e1a58a726f27a01dfd12a2a" +dependencies = [ + "gcc", + "libc", + "rand 0.3.23", + "rustc-serialize", + "time", +] + +[[package]] +name = "rustc-serialize" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe834bc780604f4674073badbad26d7219cadfb4a2275802db12cbae17498401" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "time" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b797afad3f312d1c66a56d11d0316f916356d11bd158fbc6ca6389ff6bf805a" +dependencies = [ + "libc", + "wasi 0.10.0+wasi-snapshot-preview1", + "winapi", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "wasi" +version = "0.10.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a143597ca7c7793eff794def352d41792a93c481eb1042423ff7ff72ba2c31f" + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..e67e085 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "mayo" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +tiny-keccak = { version = "2.0", features = ["shake"] } +rand = "0.8.5" +rust-crypto = "0.2.36" +rayon = "1.8.0" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5e011eb --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,11 @@ +#![feature(adt_const_params)] +#![feature(generic_arg_infer)] +#![feature(generic_const_exprs)] +#![feature(portable_simd)] + +mod math; +mod params; +mod sig; +mod sym; + +pub mod mayo; diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..37ecc96 --- /dev/null +++ b/src/math.rs @@ -0,0 +1,330 @@ +use crate::params::{k, m, n, o, Level}; +use std::ops::{Add, Neg}; + +#[derive(Debug, Clone, Copy)] +pub struct Mat(pub [[u8; C]; R]); + +// TODO: Bitslicing (not u8's) (see below) (fix this so KATs verify) +// TODO: Finish Simd optimization +// TODO: convert these to Rayon: put loops over things into mp +// TODO: Optimize matrix Mul: Strassen or smth + +const MUL_LOOKUP: [[u8; 16]; 16] = [ + [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], + [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15], + [0, 2, 4, 6, 8, 10, 12, 14, 3, 1, 7, 5, 11, 9, 15, 13], + [0, 3, 6, 5, 12, 15, 10, 9, 11, 8, 13, 14, 7, 4, 1, 2], + [0, 4, 8, 12, 3, 7, 11, 15, 6, 2, 14, 10, 5, 1, 13, 9], + [0, 5, 10, 15, 7, 2, 13, 8, 14, 11, 4, 1, 9, 12, 3, 6], + [0, 6, 12, 10, 11, 13, 7, 1, 5, 3, 9, 15, 14, 8, 2, 4], + [0, 7, 14, 9, 15, 8, 1, 6, 13, 10, 3, 4, 2, 5, 12, 11], + [0, 8, 3, 11, 6, 14, 5, 13, 12, 4, 15, 7, 10, 2, 9, 1], + [0, 9, 1, 8, 2, 11, 3, 10, 4, 13, 5, 12, 6, 15, 7, 14], + [0, 10, 7, 13, 14, 4, 9, 3, 15, 5, 8, 2, 1, 11, 6, 12], + [0, 11, 5, 14, 10, 1, 15, 4, 7, 12, 2, 9, 13, 6, 8, 3], + [0, 12, 11, 7, 5, 9, 14, 2, 10, 6, 1, 13, 15, 3, 4, 8], + [0, 13, 9, 4, 1, 12, 8, 5, 2, 15, 11, 6, 3, 14, 10, 7], + [0, 14, 15, 1, 13, 3, 2, 12, 9, 7, 6, 8, 4, 10, 11, 5], + [0, 15, 13, 2, 9, 6, 4, 11, 1, 14, 12, 3, 8, 7, 5, 10], +]; + +#[inline] +pub fn mul(f16_1: u8, f16_2: u8) -> u8 { + MUL_LOOKUP[f16_1 as usize][f16_2 as usize] +} + +pub fn upper(mat: Mat) -> Mat { + let mut up = Mat([[0; N]; N]); + for i in 0..N { + for j in (i + 1)..N { + up.write(i, j, mat.access(i, j) + mat.access(j, i)); + } + for i in 0..N { + up.write(i, i, mat.access(i, i)); + } + } + up +} + +impl Mat { + #[inline] + pub fn access(&self, i: usize, j: usize) -> u8 { + self.0[i][j] + } + + #[inline] + pub fn write(&mut self, i: usize, j: usize, v: u8) { + self.0[i][j] = v; + } + + pub fn tp(&self) -> Mat { + let mut tp = Mat([[0; R]; C]); + for i in 0..R { + for j in 0..C { + tp.write(j, i, self.access(i, j)); + } + } + tp + } + + pub fn mul_right(&self, other: Mat) -> Mat { + let other_tp = other.tp(); + let mut out = Mat([[0; N]; R]); + for i in 0..R { + for j in 0..N { + let write = (0..C) + .map(|k| mul(other_tp.0[j][k], self.0[i][k])) + .fold(0, |acc, x| acc ^ x); + out.write(i, j, write); + } + } + out + } +} + +impl Add for Mat { + type Output = Mat; + + fn add(self, rhs: Self) -> Self::Output { + let mut out = Mat([[0; C]; R]); + for i in 0..R { + for j in 0..C { + out.write(i, j, self.access(i, j) ^ rhs.access(i, j)) + } + } + out + } +} + +impl Neg for Mat { + type Output = Mat; + + fn neg(self) -> Self::Output { + self + } +} + +pub fn decode_matrix(bs: &[u8]) -> Mat { + let mut mat = Mat([[0; C]; R]); + for i in 0..R { + for j in 0..C { + if (j + C * i) % 2 == 0 { + mat.write(i, j, (bs[(j + C * i) / 2] >> 4) & 0xf); + } else { + mat.write(i, j, bs[(j + C * i) / 2] & 0xf); + } + } + } + mat +} + +pub fn decode_matrices( + bs: &[u8], + num: usize, + triangular: bool, +) -> [Mat; M] { + let mut m = [Mat([[0; C]; R]); M]; + match triangular { + true => { + let mut ind = 0; + for s in 0..M { + for i in 0..R { + for j in i..C { + if ind % 2 == 0 { + m[s].write(i, j, (bs[ind / 2] >> 4) & 0xf); + } else { + m[s].write(i, j, bs[ind / 2] & 0xf); + } + ind += 1; + } + } + } + } + false => { + for s in 0..M { + for i in 0..R { + for j in 0..C { + if (j + C * i) % 2 == 0 { + m[s].write(i, j, (bs[(j + C * i + R * C * s) / 2] >> 4) & 0xf); + } else { + m[s].write(i, j, bs[(j + C * i + R * C * s) / 2] & 0xf); + } + } + } + } + } + } + m +} + +pub fn encode_matrices( + bs: &[Mat], + triangular: bool, +) -> Vec { + let mut outvec = vec![]; + match triangular { + true => { + let mut ind = 0; + for mat in bs { + for i in 0..R { + for j in i..C { + if ind % 2 == 0 { + outvec.push(mat.access(i, j) << 4); + } else { + outvec[ind / 2] |= mat.access(i, j); + } + ind += 1; + } + } + } + } + false => { + for mat in bs { + for i in 0..R { + for j in 0..C { + if (j + C * i) % 2 == 0 { + outvec.push(mat.access(i, j) << 4); + } else { + outvec[(j + C * i) / 2] |= mat.access(i, j); + } + } + } + } + } + } + outvec +} + +pub fn decode_vec(bs: &[u8]) -> Mat { + let mut ret = Mat([[0; 1]; R]); + for i in 0..((R + 1) / 2) { + ret.write(2 * i, 0, (bs[i] >> 4) & 0xf); + if 2 * i + 1 < R { + ret.write(2 * i + 1, 0, bs[i] & 0xf); + } + } + ret +} + +pub fn encode_vec(vec: Mat) -> [u8; (R + 1) / 2] { + let mut out = [0; (R + 1) / 2]; + for i in 0..((R + 1) / 2) { + out[i] = vec.access(2 * i, 0) << 4; + if 2 * i + 1 < R { + out[i] |= vec.access(2 * i + 1, 0); + } + } + out +} + +pub fn sample_solution( + mat: Mat<{ m(L) }, { k(L) * o(L) }>, + y: Mat<{ m(L) }, 1>, + r: Mat<{ k(L) * o(L) }, 1>, +) -> Mat<{ n(L) }, 1> { + todo!() +} + +// pub struct FullMat([[u8; C]; R]); +// +// pub const fn tpn(n: usize) -> usize { +// (n * (n + 1)) / 2 +// } +// +// pub struct TMat([u8; tpn(N)]) +// where +// [(); tpn(N)]:; +// +// pub enum Mat +// where +// [(); tpn(R)]:, +// { +// Full(FullMat), +// Upper(TMat), +// } +// +// pub fn upper(mat: Mat) -> Mat +// where +// [(); tpn(N)]:, +// { +// match mat { +// Mat::Full(f) => { +// let mut up = Mat::Upper(TMat([0; tpn(N)])); +// for i in 0..N { +// for j in (i + 1)..N { +// up.write(i, j, mat.access(i, j) + mat.access(j, i)); +// } +// } +// for i in 0..N { +// up.write(i, i, mat.access(i, i)); +// } +// up +// } +// Mat::Upper(u) => mat, +// } +// } +// +// impl Mat +// where +// [(); tpn(R)]:, +// [(); tpn(C)]:, +// { +// pub fn access(&self, i: usize, j: usize) -> u8 { +// match self { +// Mat::Full(f) => f.0[i][j], +// Mat::Upper(u) => { +// if i > j { +// 0 +// } else { +// u.0[tpn(R) - tpn(R - i) + j - i] +// } +// } +// } +// } +// +// pub fn write(&mut self, i: usize, j: usize, v: u8) { +// match self { +// Mat::Full(f) => { +// f.0[i][j] = v; +// } +// Mat::Upper(u) => { +// if i <= j { +// u.0[tpn(R) - tpn(R - i) + j - i] = v; +// } +// } +// } +// } +// +// pub fn tp(&self) -> Mat { +// todo!() +// } +// } +// +// pub fn decode_matrix(bs: &[u8]) -> Mat +// where +// [(); tpn(R)]:, +// { +// todo!() +// }) +// +// pub fn decode_matrices( +// bs: &[u8], +// num: usize, +// triangular: bool, +// ) -> [Mat; M] +// where +// [(); tpn(R)]:, +// { +// todo!() +// } + +#[cfg(test)] +mod test { + use crate::math::{upper, Mat}; + #[test] + fn test_upper() { + let m = Mat([[0, 1, 2], [3, 4, 5], [6, 7, 8]]); + println!("{:?}", upper(m)); + assert!(true); + } +} diff --git a/src/mayo.rs b/src/mayo.rs new file mode 100644 index 0000000..d839db5 --- /dev/null +++ b/src/mayo.rs @@ -0,0 +1,278 @@ +use crate::math::{ + decode_matrices, decode_matrix, decode_vec, encode_matrices, encode_vec, sample_solution, + upper, Mat, +}; +use crate::params::Level; +use crate::params::{ + digest_bytes, k, l_bytes, m, n, o, o_bytes, p1_bytes, p2_bytes, p3_bytes, pk_seed_bytes, + polymat, salt_bytes, v_bytes, +}; +use crate::sig::{KeyGen, Signer, Verifier}; +use crate::sym::{aes_128_ctr, shake_256}; +use rand::rngs::OsRng; +use rand::RngCore; +use rayon::prelude::*; + +#[derive(Debug)] +pub struct SigningKey([u8; salt_bytes(L)], VerifyKey) +where + [(); salt_bytes(L)]:, + [(); pk_seed_bytes(L) + p3_bytes(L)]:; + +#[derive(Debug)] +pub struct VerifyKey([u8; pk_seed_bytes(L) + p3_bytes(L)]) +where + [(); pk_seed_bytes(L) + p3_bytes(L)]:; + +impl KeyGen for SigningKey +where + [(); salt_bytes(L)]:, + [(); pk_seed_bytes(L) + p3_bytes(L)]:, + [(); o_bytes(L) + pk_seed_bytes(L)]:, + [(); n(L) - o(L)]:, + [(); m(L)]:, +{ + fn gen() -> Self { + let mut seed_sk: [u8; salt_bytes(L)] = [0; salt_bytes(L)]; + OsRng.fill_bytes(&mut seed_sk); + + let s: [u8; o_bytes(L) + pk_seed_bytes(L)] = shake_256(vec![&seed_sk]); + + let seed_pk = &s[0..pk_seed_bytes(L)]; + + let o_bs = &s[pk_seed_bytes(L)..(pk_seed_bytes(L) + o_bytes(L))]; + let om: Mat<{ n(L) - o(L) }, { o(L) }> = + decode_matrix::<{ n(L) - o(L) }, { o(L) }, { L }>(o_bs); + + let p = aes_128_ctr(seed_pk, p1_bytes(L) + p2_bytes(L)); + + let p1: [Mat<{ n(L) - o(L) }, { n(L) - o(L) }>; m(L)] = + decode_matrices::<{ n(L) - o(L) }, { n(L) - o(L) }, { m(L) }, { L }>( + &p[0..p1_bytes(L)], + m(L), + true, + ); + let p2: [Mat<{ n(L) - o(L) }, { o(L) }>; m(L)] = + decode_matrices::<{ n(L) - o(L) }, { o(L) }, { m(L) }, { L }>( + &p[p1_bytes(L)..(p1_bytes(L) + p2_bytes(L))], + m(L), + false, + ); + + let p3 = (0..m(L)) + .into_par_iter() + .map(|i| upper(-om.tp().mul_right(p1[i].mul_right(om)) + (-om.tp().mul_right(p2[i])))) + .collect::>>(); + + let p3_enc = encode_matrices::<{ o(L) }, { o(L) }, { m(L) }, { L }>(&p3, true); + let mut out = [0; pk_seed_bytes(L) + p3_bytes(L)]; + out[..pk_seed_bytes(L)].copy_from_slice(&seed_pk[..pk_seed_bytes(L)]); + out[pk_seed_bytes(L)..].copy_from_slice(&p3_enc); + SigningKey(seed_sk, VerifyKey(out)) + } +} + +pub fn expand_sk( + csk: [u8; salt_bytes(L)], +) -> [u8; salt_bytes(L) + o_bytes(L) + p1_bytes(L) + l_bytes(L)] +where + [(); salt_bytes(L)]:, + [(); pk_seed_bytes(L) + p3_bytes(L)]:, + [(); o_bytes(L) + pk_seed_bytes(L)]:, + [(); n(L) - o(L)]:, + [(); m(L)]:, +{ + let seed_sk = csk; + + let s: [u8; o_bytes(L) + pk_seed_bytes(L)] = shake_256(vec![&seed_sk]); + + let seed_pk = &s[0..pk_seed_bytes(L)]; + + let o_bs = &s[pk_seed_bytes(L)..(pk_seed_bytes(L) + o_bytes(L))]; + let om: Mat<{ n(L) - o(L) }, { o(L) }> = + decode_matrix::<{ n(L) - o(L) }, { o(L) }, { L }>(o_bs); + + let p = aes_128_ctr(seed_pk, p1_bytes(L) + p2_bytes(L)); + + let p1: [Mat<{ n(L) - o(L) }, { n(L) - o(L) }>; m(L)] = decode_matrices::< + { n(L) - o(L) }, + { n(L) - o(L) }, + { m(L) }, + { L }, + >(&p[0..p1_bytes(L)], m(L), true); + + let p1_bm = encode_matrices::<{ n(L) - o(L) }, { n(L) - o(L) }, { m(L) }, { L }>(&p1, true); + + let p2: [Mat<{ n(L) - o(L) }, { o(L) }>; m(L)] = + decode_matrices::<{ n(L) - o(L) }, { o(L) }, { m(L) }, { L }>( + &p[p1_bytes(L)..(p1_bytes(L) + p2_bytes(L))], + m(L), + false, + ); + + let l = (0..m(L)) + .into_par_iter() + .map(|i| (p1[i] + p1[i].tp()).mul_right(om) + p2[i]) + .collect::>>(); + + let l_bs = encode_matrices::<{ n(L) - o(L) }, { o(L) }, { m(L) }, { L }>(&l, false); + let mut out = [0; salt_bytes(L) + o_bytes(L) + p1_bytes(L) + l_bytes(L)]; + out[..salt_bytes(L)].copy_from_slice(&seed_sk); + out[salt_bytes(L)..(salt_bytes(L) + o_bytes(L))].copy_from_slice(o_bs); + out[(salt_bytes(L) + o_bytes(L))..(salt_bytes(L) + o_bytes(L) + p1_bytes(L))] + .copy_from_slice(&p1_bm); + out[(salt_bytes(L) + o_bytes(L) + p1_bytes(L))..].copy_from_slice(&l_bs); + out +} + +impl Signer> for SigningKey +where + [(); salt_bytes(L)]:, + [(); pk_seed_bytes(L) + p3_bytes(L)]:, + [(); o_bytes(L) + pk_seed_bytes(L)]:, + [(); n(L) - o(L)]:, + [(); k(L) * n(L)]:, + [(); m(L)]:, + [(); m(L) / 2]:, + [(); (k(L) * n(L) + 1) / 2]:, + [(); k(L) * v_bytes(L) + ((k(L) * o(L) + 1) / 2)]:, + [(); salt_bytes(L) + o_bytes(L) + p1_bytes(L) + l_bytes(L)]:, + [(); digest_bytes(L)]:, +{ + fn sign(&self, msg: &[u8]) -> Vec { + let esk = expand_sk(self.0); + let seed_sk = &esk[..salt_bytes(L)]; + let om: Mat<{ n(L) - o(L) }, { o(L) }> = decode_matrix::<{ n(L) - o(L) }, { o(L) }, { L }>( + &esk[salt_bytes(L)..(salt_bytes(L) + o_bytes(L))], + ); + let p1: [Mat<{ n(L) - o(L) }, { n(L) - o(L) }>; m(L)] = + decode_matrices::<{ n(L) - o(L) }, { n(L) - o(L) }, { m(L) }, { L }>( + &esk[(salt_bytes(L) + o_bytes(L))..(salt_bytes(L) + o_bytes(L) + p1_bytes(L))], + m(L), + true, + ); + let li: [Mat<{ n(L) - o(L) }, { o(L) }>; m(L)] = + decode_matrices::<{ n(L) - o(L) }, { o(L) }, { m(L) }, { L }>( + &esk[(salt_bytes(L) + o_bytes(L) + p1_bytes(L))..], + m(L), + false, + ); + + let m_digest: [u8; digest_bytes(L)] = shake_256(vec![msg]); + + let salt: [u8; salt_bytes(L)] = shake_256(vec![&m_digest, seed_sk]); + + let t_bs: [u8; m(L) / 2] = shake_256(vec![&m_digest, &salt]); + + let mut t = decode_vec::<{ m(L) }>(&t_bs); + let v: [u8; k(L) * v_bytes(L) + ((k(L) * o(L) + 1) / 2)] = + shake_256(vec![&m_digest, &salt, seed_sk, &[128]]); + let mut vv: Vec> = vec![]; + for i in 0..k(L) { + vv.push(decode_vec::<{ n(L) - o(L) }>( + &v[(i * v_bytes(L))..((i + 1) * v_bytes(L))], + )); + } + let r = decode_vec::<{ k(L) * o(L) }>(&v[(k(L) * v_bytes(L))..]); + + let mut A = Mat([[0; { m(L) }]; { k(L) * o(L) }]); + + let mut l = 0; + + let mut el = Mat([[0; m(L)]; m(L)]); + for i in 0..m(L) { + el.write(i, i, 1); + } + + let mut Mi = [Mat([[0; { o(L) }]; { m(L) }]); k(L)]; + + for i in 0..k(L) { + Mi[i] = Mat([[0; { o(L) }]; { m(L) }]); + for j in 0..m(L) { + Mi[i].0[j] = (vv[i].tp().mul_right(li[i])).0[0]; + } + } + + for i in 0..k(L) { + for j in (i..(k(L))).rev() { + let mut u: Mat<{ m(L) }, 1> = Mat([[0; 1]; m(L)]); + if j == i { + for a in 0..m(L) { + u.write(a, 0, vv[i].tp().mul_right(p1[a]).mul_right(vv[i]).0[0][0]); + } + } else { + for a in 0..m(L) { + u.write( + a, + 0, + (vv[i].tp().mul_right(p1[a]).mul_right(vv[j]) + + vv[j].tp().mul_right(p1[a]).mul_right(vv[i])) + .0[0][0], + ); + } + } + t = t + (-el.mul_right(u)); + for z in (i * o(L))..((i + 1) * o(L)) { + for y in 0..(m(L)) { + A.write(y, z, A.access(y, z) + el.mul_right(Mi[j]).0[y][z]); + } + } + + if i != j { + for z in (j * o(L))..((j + 1) * o(L)) { + for y in 0..(m(L)) { + A.write(y, z, A.access(y, z) + el.mul_right(Mi[i]).0[y][z]); + } + } + } + l += 1; + el = el.mul_right(Mat(polymat())); + } + } + + let x = sample_solution(A.tp(), t, r).tp(); + + let mut s = Mat([[0; k(L) * n(L)]; 1]); + for i in 0..(k(L)) { + let x_inner = &x.0[0][(i * o(L))..((i + 1) * o(L))]; + let mut xi = [0; o(L)]; + for i in 0..o(L) { + xi[i] = x_inner[i]; + } + s.0[0][(i * n(L))..((i + 1) * n(L) - o(L))] + .copy_from_slice(&(vv[i] + (om.mul_right(Mat([xi; 1]).tp()))).0[0]); + s.0[0][((i + 1) * n(L) - o(L))..((i + 1) * n(L))] + .copy_from_slice(&x.0[0][(i * o(L))..((i + 1) * o(L))]); + } + + let mut sig = encode_vec::<{ k(L) * n(L) }>(s.tp()).to_vec(); + + for i in salt { + sig.push(i); + } + + return sig; + } +} + +impl Verifier> for VerifyKey +where + [(); pk_seed_bytes(L) + p3_bytes(L)]:, +{ + fn verify(&self, sig: &Vec, msg: &[u8]) -> bool { + todo!(); + } +} + +#[cfg(test)] +mod tests { + use super::SigningKey; + use crate::params::Level; + use crate::sig::KeyGen; + + #[test] + fn test_gen() { + let s: SigningKey<{ Level::MAYO1 }> = SigningKey::gen(); + println!("{:?}", s); + } +} diff --git a/src/naive/mod.rs b/src/naive/mod.rs new file mode 100644 index 0000000..fb571c8 --- /dev/null +++ b/src/naive/mod.rs @@ -0,0 +1,52 @@ +use crate::sig::KeyGen; +use aes::Aes128; +use rand::RngCore; +use tiny_keccak::{Shake, Xof}; + +pub struct SigParams { + m: u16, + n: u16, + o: u16, + k: u16, + salt_bytes: u8, + digest_bytes: u8, + pk_seed_bytes: u8, +} + +// sk_seed_bytes = salt_bytes +// R_bytes = salt_bytes +// O_bytes = ceil((n - o)(o)/2) +// v_bytes = ceil((n - o)/2) +// P1_bytes = m*binom((n - o + 1), 2) / 2 +// P2_bytes = m * (n - o) * o / 2 +// P3_bytes = m * binom((o + 1), 2) / 2 +// L_bytes = m * (n - o) * o / 2 +// csk_bytes = salt_bytes +// esk_bytes = salt_bytes + O_bytes + P1_bytes + L_bytes +// cpk_bytes = pk_seed_bytes + P3_bytes +// epk_bytes = P1_bytes + P2_bytes + P3_bytes +// sig_bytes = ceil(nk / 2) + salt_bytes + +pub struct SigningKey { + v: VerifyKey, +} + +pub struct VerifyKey {} + +impl<'a> KeyGen<'a, SigParams> for SigningKey { + fn gen(params: SigParams, rand: &'a mut dyn RngCore) -> Self { + let sk_seed_bytes = params.salt_bytes as usize; + + // Pick seed_sk at random + let mut seed_sk: Vec = vec![0; sk_seed_bytes]; + rand.fill_bytes(&mut seed_sk); + + // Derive seed_pk and O from seed_sk + let mut shake = Shake::v256(); + let O_bytes = ((((params.n - params.o) as usize) * (params.o as usize)) + 1) / 2; + let mut S = vec![0; params.pk_seed_bytes as usize + O_bytes]; + shake.squeeze(&mut S); + + SigningKey { v: VerifyKey {} } + } +} diff --git a/src/params.rs b/src/params.rs new file mode 100644 index 0000000..81534e9 --- /dev/null +++ b/src/params.rs @@ -0,0 +1,119 @@ +use std::marker::ConstParamTy; + +#[derive(ConstParamTy, PartialEq, Eq, Clone, Copy)] +pub enum Level { + MAYO1, + MAYO2, + MAYO3, + MAYO5, +} + +pub const fn n(level: Level) -> usize { + match level { + Level::MAYO1 => 66, + Level::MAYO2 => 78, + Level::MAYO3 => 99, + Level::MAYO5 => 133, + } +} + +pub const fn m(level: Level) -> usize { + match level { + Level::MAYO1 => 64, + Level::MAYO2 => 64, + Level::MAYO3 => 99, + Level::MAYO5 => 128, + } +} + +pub const fn o(level: Level) -> usize { + match level { + Level::MAYO1 => 8, + Level::MAYO2 => 18, + Level::MAYO3 => 10, + Level::MAYO5 => 12, + } +} + +pub const fn k(level: Level) -> usize { + match level { + Level::MAYO1 => 9, + Level::MAYO2 => 4, + Level::MAYO3 => 11, + Level::MAYO5 => 12, + } +} + +pub const fn q(level: Level) -> usize { + 16 +} + +pub const fn salt_bytes(level: Level) -> usize { + match level { + Level::MAYO1 => 24, + Level::MAYO2 => 24, + Level::MAYO3 => 32, + Level::MAYO5 => 40, + } +} + +pub const fn digest_bytes(level: Level) -> usize { + match level { + Level::MAYO1 => 32, + Level::MAYO2 => 32, + Level::MAYO3 => 48, + Level::MAYO5 => 64, + } +} + +pub const fn pk_seed_bytes(_level: Level) -> usize { + 16 +} + +pub const fn poly(level: Level) -> usize { + match level { + Level::MAYO1 => 64, + Level::MAYO2 => 64, + Level::MAYO3 => 96, + Level::MAYO5 => 128, + } +} + +pub const fn polymat() -> [[u8; m(L)]; m(L)] { + match poly(L) { + 64 => { + todo!() + } + 96 => { + todo!() + } + 128 => { + todo!() + } + _ => unimplemented!(), + } +} + +pub const fn v_bytes(level: Level) -> usize { + (n(level) - o(level) + 1) / 2 +} + +pub const fn o_bytes(level: Level) -> usize { + (((n(level) - o(level)) * o(level)) + 1) / 2 +} + +pub const fn p1_bytes(level: Level) -> usize { + m(level) * ((n(level) - o(level) + 1) * (n(level) - o(level))) / 2 +} + +pub const fn p2_bytes(level: Level) -> usize { + m(level) * (n(level) - o(level)) * o(level) / 2 +} + +pub const fn p3_bytes(level: Level) -> usize { + m(level) * (o(level) + 1) * (o(level)) / 4 +} + +pub const fn l_bytes(level: Level) -> usize { + m(level) * (n(level) - o(level)) * o(level) / 2 +} diff --git a/src/sig.rs b/src/sig.rs new file mode 100644 index 0000000..9000103 --- /dev/null +++ b/src/sig.rs @@ -0,0 +1,11 @@ +pub trait KeyGen { + fn gen() -> Self; +} + +pub trait Signer { + fn sign(&self, msg: &[u8]) -> S; +} + +pub trait Verifier { + fn verify(&self, sig: &S, msg: &[u8]) -> bool; +} diff --git a/src/sym.rs b/src/sym.rs new file mode 100644 index 0000000..a3130d5 --- /dev/null +++ b/src/sym.rs @@ -0,0 +1,25 @@ +use crypto::aes::{self, KeySize}; +use tiny_keccak::{Hasher, Shake}; + +pub fn aes_128_ctr(seed: &[u8], l: usize) -> Vec { + let nonce = [0u8; 16]; + let mut cipher = aes::ctr(KeySize::KeySize128, seed, &nonce); + let enc = vec![0u8; l]; + let mut out = vec![0u8; l]; + cipher.process(&enc, &mut out[..]); + out +} + +pub fn shake_256(inputs: Vec<&[u8]>) -> [u8; S] { + let mut shake = Shake::v256(); + + for inp in inputs { + shake.update(inp); + } + + let mut out: [u8; S] = [0; S]; + + shake.finalize(&mut out); + + out +}