Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add LPN and GGM related #54

Merged
merged 14 commits into from
Oct 13, 2023
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ See [CONTRIBUTING.md](CONTRIBUTING.md).
## Contributors

- [TLSNotary](https://github.com/tlsnotary)
- [PADO Labs](https://github.com/pado-labs)


### Pronounciation
Expand Down
9 changes: 7 additions & 2 deletions mpz-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ itybity.workspace = true
opaque-debug.workspace = true
bcs = "0.1.5"
rand_core = "0.6.4"
bytemuck = {workspace = true, features = ["derive"]}
bytemuck = { workspace = true, features = ["derive"] }
rayon = { version = "1.7.0" }
sinui0 marked this conversation as resolved.
Show resolved Hide resolved

[dev-dependencies]
rstest.workspace = true
Expand All @@ -40,4 +41,8 @@ harness = false

[[bench]]
name = "prg"
harness = false
harness = false

[[bench]]
name = "lpn"
harness = false
19 changes: 15 additions & 4 deletions mpz-core/benches/ggm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ use mpz_core::{block::Block, ggm_tree::GgmTree};
#[allow(clippy::all)]
fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("ggm::gen::1K", move |bench| {
let depth = 11;
let depth = 10;
let ggm = GgmTree::new(depth);
let mut tree = vec![Block::ZERO; 1 << (depth - 1)];
let mut k0 = vec![Block::ZERO; depth - 1];
let mut k1 = vec![Block::ZERO; depth - 1];
let mut tree = vec![Block::ZERO; 1 << (depth)];
let mut k0 = vec![Block::ZERO; depth];
let mut k1 = vec![Block::ZERO; depth];
let seed = rand::random::<Block>();
bench.iter(|| {
black_box(ggm.gen(
Expand All @@ -19,6 +19,17 @@ fn criterion_benchmark(c: &mut Criterion) {
));
});
});

c.bench_function("ggm::reconstruction::1K", move |bench| {
let depth = 10;
let ggm = GgmTree::new(depth);
let mut tree = vec![Block::ZERO; 1 << (depth)];
let k = vec![Block::ZERO; depth];
let alpha = vec![false; depth];
bench.iter(|| {
black_box(ggm.reconstruct(black_box(&mut tree), black_box(&k), black_box(&alpha)))
});
});
}

criterion_group!(benches, criterion_benchmark);
Expand Down
103 changes: 103 additions & 0 deletions mpz-core/benches/lpn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use mpz_core::{lpn::Lpn, prg::Prg, Block};
use std::time::Duration;

fn criterion_benchmark(c: &mut Criterion) {
c.bench_function("lpn-native-small", move |bench| {
let seed = Block::ZERO;
let k = 5_060;
let n = 166_400;
let lpn = Lpn::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute_naive(&mut y, &x));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

passing a unit value to a function
for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unit_arg

});
});

c.bench_function("lpn-native-medium", move |bench| {
let seed = Block::ZERO;
let k = 158_000;
let n = 10_168_320;
let lpn = Lpn::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute_naive(&mut y, &x));
});
});

c.bench_function("lpn-native-large", move |bench| {
let seed = Block::ZERO;
let k = 588_160;
let n = 10_616_092;
let lpn = Lpn::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute_naive(&mut y, &x));
});
});

c.bench_function("lpn-rayon-small", move |bench| {
let seed = Block::ZERO;
let k = 5_060;
let n = 166_400;
let lpn = Lpn::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute(&mut y, &x));
});
});

c.bench_function("lpn-rayon-medium", move |bench| {
let seed = Block::ZERO;
let k = 158_000;
let n = 10_168_320;
let lpn = Lpn::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute(&mut y, &x));
});
});

c.bench_function("lpn-rayon-large", move |bench| {
let seed = Block::ZERO;
let k = 588_160;
let n = 10_616_092;
let lpn = Lpn::<10>::new(seed, k);
let mut x = vec![Block::ZERO; k as usize];
let mut y = vec![Block::ZERO; n];
let mut prg = Prg::new();
prg.random_blocks(&mut x);
prg.random_blocks(&mut y);
bench.iter(|| {
black_box(lpn.compute(&mut y, &x));
});
});
}

// criterion_group!(benches, criterion_benchmark);
criterion_group! {
name = lpn;
config = Criterion::default().warm_up_time(Duration::from_millis(1000)).sample_size(10);
targets = criterion_benchmark
}
criterion_main!(lpn);
50 changes: 48 additions & 2 deletions mpz-core/src/aes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl FixedKeyAes {
}

/// A wrapper of aes, only for encryption.
// Use `RUSTFLAGS="-Ctarget-cpu=native --cfg=aes_armv8"` for optimal performance.
#[derive(Clone)]
pub struct AesEncryptor(Aes128Enc);

Expand Down Expand Up @@ -99,6 +100,24 @@ impl AesEncryptor {
ctxt
}

/// Encrypt block slice.
pub fn encrypt_block_slice(&self, blks: &mut [Block]) {
let len = blks.len();
let mut buf = [Block::ZERO; AesEncryptor::AES_BLOCK_COUNT];
for i in 0..len / AesEncryptor::AES_BLOCK_COUNT {
buf.copy_from_slice(
&blks[i * AesEncryptor::AES_BLOCK_COUNT..(i + 1) * AesEncryptor::AES_BLOCK_COUNT],
);
blks[i * AesEncryptor::AES_BLOCK_COUNT..(i + 1) * AesEncryptor::AES_BLOCK_COUNT]
.copy_from_slice(&self.encrypt_many_blocks(buf));
}

let remain = len % AesEncryptor::AES_BLOCK_COUNT;
for block in blks[len - remain..].iter_mut() {
*block = self.encrypt_block(*block);
}
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #59

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@xiangxiecrypto you should be able to rebase on dev and use the existing impl for this now


/// Encrypt many blocks with many keys.
/// Input: `NK` AES keys `keys`, and `NK * NM` blocks `blks`
/// Output: each batch of NM blocks encrypted by a corresponding AES key.
Expand All @@ -116,10 +135,37 @@ impl AesEncryptor {

#[test]
fn aes_test() {
let aes = AesEncryptor::new(Block::default());
let aes = AesEncryptor::new(Block::ZERO);
let aes1 = AesEncryptor::new(Block::ONES);

let mut blks = [Block::default(); 4];
let c = aes.encrypt_block(Block::ZERO);
let res = Block::from(0x2e2b34ca59fa4c883b2c8aefd44be966_u128.to_le_bytes());
assert_eq!(c, res);

macro_rules! encrypt_test {
($n:expr) => {{
let blks = [Block::ZERO; $n];

let d = aes.encrypt_many_blocks(blks);
assert_eq!(d, [res; $n]);

let mut f = [Block::ZERO; $n];
aes.encrypt_block_slice(&mut f);
assert_eq!(f, [res; $n]);
}};
}

encrypt_test!(1);
encrypt_test!(2);
encrypt_test!(3);
encrypt_test!(4);
encrypt_test!(5);
encrypt_test!(6);
encrypt_test!(7);
encrypt_test!(8);
encrypt_test!(9);

let mut blks = [Block::ZERO; 4];
blks[1] = Block::ONES;
blks[3] = Block::ONES;
AesEncryptor::para_encrypt::<2, 2>(&[aes, aes1], &mut blks);
Expand Down
128 changes: 91 additions & 37 deletions mpz-core/src/ggm_tree.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ impl GgmTree {
Self { tkprp, depth }
}

/// Input: `seed`: a seed.
/// Output: `tree`: a GGM (binary tree) `tree`, with size `2^{depth-1}`
/// Output: `k0`: XORs of all the left-node values in each level, with size `depth-1`.
/// Output: `k1`: XORs of all the right-node values in each level, with size `depth-1`.
/// Input : `seed` - a seed.
/// Output: `tree` - a GGM (binary tree) `tree`, with size `2^{depth}`.
/// Output: `k0` - XORs of all the left-node values in each level, with size `depth`.
/// Output: `k1`- XORs of all the right-node values in each level, with size `depth`.
/// This implementation is adapted from EMP Toolkit.
pub fn gen(&self, seed: Block, tree: &mut [Block], k0: &mut [Block], k1: &mut [Block]) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Input : `seed` - a seed.
/// Output: `tree` - a GGM (binary tree) `tree`, with size `2^{depth}`.
/// Output: `k0` - XORs of all the left-node values in each level, with size `depth`.
/// Output: `k1`- XORs of all the right-node values in each level, with size `depth`.
/// This implementation is adapted from EMP Toolkit.
pub fn gen(&self, seed: Block, tree: &mut [Block], k0: &mut [Block], k1: &mut [Block]) {
/// Generate a GGM tree in-place.
///
/// # Arguments
///
/// * `seed` - a seed.
/// * `tree` - the destination to write the GGM (binary tree) `tree`, with size `2^{depth}`.
/// * `k0` - XORs of all the left-node values in each level, with size `depth`.
/// * `k1`- XORs of all the right-node values in each level, with size `depth`.
pub fn gen(&self, seed: Block, tree: &mut [Block], k0: &mut [Block], k1: &mut [Block]) {
// This implementation is adapted from EMP Toolkit.

assert!(tree.len() == 1 << (self.depth - 1));
assert!(k0.len() == self.depth - 1);
assert!(k1.len() == self.depth - 1);
assert!(tree.len() == 1 << (self.depth));
assert!(k0.len() == self.depth);
assert!(k1.len() == self.depth);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
assert!(tree.len() == 1 << (self.depth));
assert!(k0.len() == self.depth);
assert!(k1.len() == self.depth);
assert_eq!(tree.len(), 1 << (self.depth));
assert_eq!(k0.len(), self.depth);
assert_eq!(k1.len(), self.depth);

let mut buf = vec![Block::ZERO; 8];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can avoid this heap allocation

Suggested change
let mut buf = vec![Block::ZERO; 8];
let mut buf = [Block::ZERO; 8];

self.tkprp.expand_1to2(tree, seed);
k0[0] = tree[0];
Expand All @@ -37,7 +37,7 @@ impl GgmTree {
k1[1] = buf[1] ^ buf[3];
tree[0..4].copy_from_slice(&buf[0..4]);

for h in 2..self.depth - 1 {
for h in 2..self.depth {
k0[h] = Block::ZERO;
k1[h] = Block::ZERO;
let sz = 1 << h;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let sz = 1 << h;
// How many nodes there are in this layer
let sz = 1 << h;

Expand All @@ -56,43 +56,97 @@ impl GgmTree {
}
}
}

/// Reconstruct the GGM tree except the value in a given position.
/// Input : `k` - a slice of blocks with length `depth`, the values of k are chosen via OT from k0 and k1. For the i-th value, if alpha[i] == 1, k[i] = k1[i]; else k[i] = k0[i].
/// Input : `alpha` - a slice of bits with length `depth`.
/// Output : `tree` - the ggm tree, except `tree[pos] == Block::ZERO`. The bit decomposition of `pos` is the complement of `alpha`. I.e., `pos[i] = 1 xor alpha[i]`.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// Reconstruct the GGM tree except the value in a given position.
/// Input : `k` - a slice of blocks with length `depth`, the values of k are chosen via OT from k0 and k1. For the i-th value, if alpha[i] == 1, k[i] = k1[i]; else k[i] = k0[i].
/// Input : `alpha` - a slice of bits with length `depth`.
/// Output : `tree` - the ggm tree, except `tree[pos] == Block::ZERO`. The bit decomposition of `pos` is the complement of `alpha`. I.e., `pos[i] = 1 xor alpha[i]`.
/// Reconstruct the GGM tree except the value in a given position.
///
/// This reconstructs the GGM tree entirely except `tree[pos] == Block::ZERO`. The bit decomposition of `pos` is the complement of `alpha`. i.e., `pos[i] = 1 xor alpha[i]`.
///
/// # Arguments
///
/// * `k` - a slice of blocks with length `depth`, the values of k are chosen via OT from k0 and k1. For the i-th value, if alpha[i] == 1, k[i] = k1[i]; else k[i] = k0[i].
/// * `alpha` - a slice of bits with length `depth`.
/// * `tree` - the destination to write the GGM tree.

pub fn reconstruct(&self, tree: &mut [Block], k: &[Block], alpha: &[bool]) {
let mut pos = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we want some asserts on tree.len(), k.len(), alpha.len() similarly like like in gen() above ?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, we need that asserts.

for i in 1..=self.depth {
pos *= 2;
tree[pos] = Block::ZERO;
tree[pos + 1] = Block::ZERO;
if !alpha[i - 1] {
self.reconstruct_layer(i, false, pos, k[i - 1], tree);
pos += 1;
} else {
self.reconstruct_layer(i, true, pos + 1, k[i - 1], tree);
}
}
}

// Handle each layer.
fn reconstruct_layer(
&self,
depth: usize,
left_or_right: bool,
pos: usize,
k: Block,
tree: &mut [Block],
) {
let sz = 1 << depth;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let sz = 1 << depth;
// How many nodes there are in this layer
let sz = 1 << depth;

let mut sum = Block::ZERO;
let start = if left_or_right { 1 } else { 0 };

for i in (start..sz).step_by(2) {
sum ^= tree[i];
}
tree[pos] = sum ^ k;

if depth == (self.depth) {
return;
}

let mut buf = vec![Block::ZERO; 8];
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let mut buf = vec![Block::ZERO; 8];
let mut buf = [Block::ZERO; 8];

if sz == 2 {
self.tkprp.expand_2to4(&mut buf, tree);
tree[0..4].copy_from_slice(&buf[0..4]);
} else {
for i in (0..=sz - 4).rev().step_by(4) {
self.tkprp.expand_4to8(&mut buf, &tree[i..]);
tree[2 * i..2 * i + 8].copy_from_slice(&buf);
}
}
}
}

#[test]
fn ggm_test() {
use crate::ggm_tree::GgmTree;
use crate::Block;

let depth = 3;
let mut tree = vec![Block::ZERO; 1 << (depth - 1)];
let mut k0 = vec![Block::ZERO; depth - 1];
let mut k1 = vec![Block::ZERO; depth - 1];
let mut tree = vec![Block::ZERO; 1 << depth];
let mut k0 = vec![Block::ZERO; depth];
let mut k1 = vec![Block::ZERO; depth];
let mut k = vec![Block::ZERO; depth];
let alpha = [false, true, false];
let mut pos = 0;

for a in alpha {
pos <<= 1;
if !a {
pos += 1;
}
}

let ggm = GgmTree::new(depth);

ggm.gen(Block::ZERO, &mut tree, &mut k0, &mut k1);

// Test vectors are from EMP Toolkit.
assert_eq!(
tree,
[
Block::from(0x92A6DDEAA3E99F9BECB268BD9EF67C91_u128.to_le_bytes()),
Block::from(0x9E7E9C02ED1E62385EE8A9EDDC63A2B5_u128.to_le_bytes()),
Block::from(0xBD4B85E90AACBD106694537DB6251264_u128.to_le_bytes()),
Block::from(0x230485DC4360014833E07D8D914411A2_u128.to_le_bytes()),
]
);

assert_eq!(
k0,
[
Block::from(0x2E2B34CA59FA4C883B2C8AEFD44BE966_u128.to_le_bytes()),
Block::from(0x2FED5803A945228B8A263BC028D36EF5_u128.to_le_bytes()),
]
);

assert_eq!(
k1,
[
Block::from(0x7E46C568D1CD4972BB1A61F95DD80EDC_u128.to_le_bytes()),
Block::from(0xBD7A19DEAE7E63706D08D4604D27B317_u128.to_le_bytes()),
]
);
for i in 0..depth {
if alpha[i] {
k[i] = k1[i];
} else {
k[i] = k0[i];
}
}

let mut tree_reconstruct = vec![Block::ZERO; 1 << depth];
ggm.reconstruct(&mut tree_reconstruct, &k, &alpha);

assert_eq!(tree_reconstruct[pos], Block::ZERO);
tree_reconstruct[pos] = tree[pos];
assert_eq!(tree, tree_reconstruct);
}
Loading