Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/threshold-signatures/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ futures.workspace = true
futures-lite.workspace = true
hex.workspace = true
hkdf.workspace = true
itertools.workspace = true
k256 = { workspace = true, features = [
"sha256",
"ecdsa",
Expand Down
15 changes: 12 additions & 3 deletions crates/threshold-signatures/src/crypto/proofs/strobe.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@

//! Minimal implementation of (parts of) Strobe.

// TODO(#122): remove this exception
#![allow(clippy::indexing_slicing)]

use derive_more::{Deref, DerefMut};
use zeroize::Zeroize;

use crate::crypto::constants::{FLAG_A, FLAG_C, FLAG_I, FLAG_K, FLAG_M, FLAG_T, STROBE_R};

// SAFETY: `KeccakState` is `[u8; 200]`. These functions iterate `i` in `0..25`,
// indexing `st[8*i..8*i+8]` — the maximum accessed byte is `8*24 + 7 = 199`,
// which is within bounds.
#[allow(clippy::indexing_slicing)]
fn transmute_state(st: &KeccakState) -> [u64; 25] {
let mut result = [0u64; 25];
for (i, resulti) in result.iter_mut().enumerate() {
Expand All @@ -23,6 +24,7 @@ fn transmute_state(st: &KeccakState) -> [u64; 25] {
result
}

#[allow(clippy::indexing_slicing)]
fn untransmute_state(transmuted_state: [u64; 25], state: &mut KeccakState) {
for (i, ti) in transmuted_state.iter().enumerate() {
state[8 * i..8 * i + 8].copy_from_slice(&ti.to_le_bytes());
Expand Down Expand Up @@ -58,6 +60,7 @@ impl ::core::fmt::Debug for Strobe128 {
}

impl Strobe128 {
#[allow(clippy::indexing_slicing)] // Constant slices of [u8; 200]: [0..6] and [6..18]
pub fn new(protocol_label: &[u8]) -> Self {
let initial_state = {
let mut st = KeccakState([0u8; 200]);
Expand Down Expand Up @@ -100,6 +103,12 @@ impl Strobe128 {
}
}

// SAFETY for all methods in this block: `self.state` is `[u8; 200]` and `STROBE_R = 166`.
// - `absorb`, `overwrite`, `squeeze`: `self.pos` ranges from 0 to `STROBE_R - 1` (reset to 0
// via `run_f` when `pos == STROBE_R`), so `self.state[self.pos as usize]` is always in bounds.
// - `run_f`: called when `pos <= STROBE_R = 166`, so `pos + 1 <= 167 < 200` and
// `STROBE_R + 1 = 167 < 200` are both in bounds.
#[allow(clippy::indexing_slicing)]
impl Strobe128 {
fn run_f(&mut self) {
self.state[self.pos as usize] ^= self.pos_begin;
Expand Down
3 changes: 0 additions & 3 deletions crates/threshold-signatures/src/ecdsa/ot_based_ecdsa.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
// TODO(#122): remove this exception
#![allow(clippy::indexing_slicing)]

pub mod presign;
pub mod sign;
pub mod triples;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,22 @@ pub async fn batch_random_ot_sender_many<const N: usize>(
let wait0 = chan.next_waitpoint();
let big_x_i_verkey_v: Vec<CoefficientCommitment> = chan.recv(wait0).await?;

if big_x_i_verkey_v.len() < N {
return Err(ProtocolError::AssertionFailed(format!(
"received vector of length {}, expected at least {N}",
big_x_i_verkey_v.len(),
)));
}

let mut ret = vec![];
for (j, big_x_i_verkey_v_j) in big_x_i_verkey_v.iter().enumerate().take(N) {
let y = &yv_arc.as_slice()[j];
let big_y_verkey = &big_y_verkey_v_arc.as_slice()[j];
let big_z = &big_z_v_arc.as_slice()[j];
for (big_x_i_verkey_v_j, ((y, big_y_verkey), big_z)) in
big_x_i_verkey_v.iter().take(N).zip(
yv_arc
.iter()
.zip(big_y_verkey_v_arc.iter())
.zip(big_z_v_arc.iter()),
)
{
let y_big_x_i = big_x_i_verkey_v_j.value() * *y;
let big_k0 = hash(
i,
Expand All @@ -180,8 +191,14 @@ pub async fn batch_random_ot_sender_many<const N: usize>(
reshaped_outs.push(Vec::new());
}
for outsi in outs {
for j in 0..N {
reshaped_outs[j].push(outsi[j]);
if outsi.len() < N {
return Err(ProtocolError::AssertionFailed(format!(
"task output has length {}, expected at least {N}",
outsi.len(),
)));
}
for (reshaped, val) in reshaped_outs.iter_mut().zip(outsi.iter()) {
reshaped.push(*val);
}
}
let outs = reshaped_outs;
Expand Down Expand Up @@ -231,12 +248,10 @@ pub(super) async fn batch_random_ot_receiver(

let out = delta
.bits()
.zip(x.iter())
.enumerate()
.map(|(i, d_i)| {
.map(|(i, (d_i, &x_i))| {
let mut chan = chan.child(i as u64);
// Step 4
// let x_i = Secp256K1ScalarField::random(&mut rng);
let x_i = x[i];
let mut big_x_i = ProjectivePoint::GENERATOR * x_i;
big_x_i.conditional_assign(&(big_x_i + big_y), d_i);

Expand All @@ -262,6 +277,7 @@ pub(super) async fn batch_random_ot_receiver(
}

#[allow(dead_code)]
#[allow(clippy::too_many_lines)]
pub async fn batch_random_ot_receiver_many<const N: usize>(
mut chan: PrivateChannel,
mut rng: impl CryptoRngCore,
Expand All @@ -272,6 +288,13 @@ pub async fn batch_random_ot_receiver_many<const N: usize>(
// deserialization prevents receiving the identity
let big_y_verkey_v: Vec<CoefficientCommitment> = chan.recv(wait0).await?;

if big_y_verkey_v.len() < N {
return Err(ProtocolError::AssertionFailed(format!(
"received big_y_verkey_v of length {}, expected at least {N}",
big_y_verkey_v.len(),
)));
}

let mut big_y_v = vec![];
let mut deltav = vec![];
for big_y_verkey in &big_y_verkey_v {
Expand All @@ -285,13 +308,14 @@ pub async fn batch_random_ot_receiver_many<const N: usize>(
let big_y_verkey_v_arc = Arc::new(big_y_verkey_v);

// inner is batch, outer is bits
let mut choices: Vec<Vec<_>> = Vec::new();
for _ in deltav[0].bits() {
choices.push(Vec::new());
}
let first_delta = deltav
.first()
.ok_or_else(|| ProtocolError::AssertionFailed("deltav must be non-empty".to_string()))?;
let num_bits = first_delta.bits().count();
let mut choices: Vec<Vec<_>> = (0..num_bits).map(|_| Vec::new()).collect();
for deltavj in deltav.iter().take(N) {
for (i, d_i) in deltavj.bits().enumerate() {
choices[i].push(d_i);
for (choice_vec, d_i) in choices.iter_mut().zip(deltavj.bits()) {
choice_vec.push(d_i);
}
}
// wrap in arc
Expand All @@ -307,36 +331,35 @@ pub async fn batch_random_ot_receiver_many<const N: usize>(
let hashv = {
let mut x_i_v = Vec::new();
let mut big_x_i_v = Vec::new();
for j in 0..N {
let d_i = d_i_v[j];
for (d_i, big_y) in d_i_v.iter().take(N).zip(big_y_v_arc.iter()) {
// Step 4
let x_i = Secp256K1ScalarField::random(&mut rng);
let mut big_x_i = ProjectivePoint::GENERATOR * x_i;
big_x_i.conditional_assign(&(big_x_i + big_y_v_arc[j]), d_i);
big_x_i.conditional_assign(&(big_x_i + big_y), *d_i);
x_i_v.push(x_i);
big_x_i_v.push(big_x_i);
}
// Step 6
let wait0 = chan.next_waitpoint();

let mut big_x_i_verkey_v = Vec::new();
for big_x_i_verkey in &big_x_i_v {
big_x_i_verkey_v.push(CoefficientCommitment::new(*big_x_i_verkey));
}
let big_x_i_verkey_v: Vec<_> = big_x_i_v
.iter()
.map(|p| CoefficientCommitment::new(*p))
.collect();
chan.send(wait0, &big_x_i_verkey_v)?;

// Step 5
let mut hashv = Vec::new();
for j in 0..N {
let big_x_i_verkey = big_x_i_verkey_v[j];
let big_y_verkey = big_y_verkey_v_arc[j];
let big_y = big_y_v_arc[j];
let x_i = x_i_v[j];
for ((big_x_i_verkey, big_y_verkey), (big_y, x_i)) in big_x_i_verkey_v
.iter()
.zip(big_y_verkey_v_arc.iter())
.zip(big_y_v_arc.iter().zip(x_i_v.iter()))
{
hashv.push(hash(
i,
&big_x_i_verkey,
&big_y_verkey,
&CoefficientCommitment::new(big_y * x_i),
big_x_i_verkey,
big_y_verkey,
&CoefficientCommitment::new(*big_y * *x_i),
)?);
}
hashv
Expand All @@ -350,19 +373,23 @@ pub async fn batch_random_ot_receiver_many<const N: usize>(
reshaped_outs.push(Vec::new());
}
for outsi in &outs {
for j in 0..N {
reshaped_outs[j].push(outsi[j]);
if outsi.len() < N {
return Err(ProtocolError::AssertionFailed(format!(
"task output has length {}, expected at least {N}",
outsi.len(),
)));
}
for (reshaped, val) in reshaped_outs.iter_mut().zip(outsi.iter()) {
reshaped.push(*val);
}
}
let outs = reshaped_outs;
let mut ret = Vec::new();
for j in 0..N {
let delta = deltav[j];
let out = &outs[j];
for (delta, out) in deltav.iter().zip(outs.iter()).take(N) {
let big_k: BitMatrix = out.iter().copied().collect();
let h = SquareBitMatrix::try_from(big_k);
let h = h.map_err(|err| ProtocolError::AssertionFailed(format!("{err:?}")))?;
ret.push((delta, h));
ret.push((*delta, h));
}
Ok(ret)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,15 @@ impl BitVector {
}

/// Get a specific bit from the vector.
///
/// # Panics
/// Panics if `j >= SECURITY_PARAMETER`.
pub fn bit(&self, j: usize) -> u8 {
// This is safe to do, result is 0 or 1
((self.0[j / 64] >> (j % 64)) & 1) as u8
let word = self
.0
.get(j / 64)
.expect("bit index must be < SECURITY_PARAMETER");
((word >> (j % 64)) & 1) as u8
}

pub fn from_bytes(bytes: &[u8; SEC_PARAM_8]) -> Self {
Expand All @@ -57,8 +63,8 @@ impl BitVector {

pub fn bytes(&self) -> [u8; SEC_PARAM_8] {
let mut out = [0u8; SEC_PARAM_8];
for (i, x_i) in self.0.iter().enumerate() {
out[8 * i..8 * (i + 1)].copy_from_slice(&x_i.to_le_bytes());
for (chunk, x_i) in out.chunks_exact_mut(8).zip(self.0.iter()) {
chunk.copy_from_slice(&x_i.to_le_bytes());
}
out
}
Expand Down Expand Up @@ -113,15 +119,15 @@ impl BitVector {
let mut out = [0u64; 2 * SEC_PARAM_64];

for k in (0..64).rev() {
for j in 0..SEC_PARAM_64 {
for (j, self_word) in self.0.iter().enumerate() {
let to_add = Self::conditional_select(
&Self::zero(),
other,
Choice::from(((self.0[j] >> k) & 1) as u8),
Choice::from(((*self_word >> k) & 1) as u8),
);

for i in 0..SEC_PARAM_64 {
out[j + i] ^= to_add.0[i];
for (out_elem, add_elem) in out.iter_mut().skip(j).zip(to_add.0.iter()) {
*out_elem ^= add_elem;
}
}
if k != 0 {
Expand Down Expand Up @@ -250,8 +256,10 @@ impl BitMatrix {
pub fn column_chunks(&self, j: usize) -> impl Iterator<Item = BitVector> + '_ {
self.0.chunks_exact(SECURITY_PARAMETER).map(move |chunk| {
let mut out = BitVector::zero();
for (i, c_i) in chunk.iter().enumerate() {
out.0[i / 64] |= u64::from(c_i.bit(j)) << (i % 64);
for (word, word_chunk) in out.0.iter_mut().zip(chunk.chunks(64)) {
for (bit_idx, c_i) in word_chunk.iter().enumerate() {
*word |= u64::from(c_i.bit(j)) << bit_idx;
}
}
out
})
Expand Down Expand Up @@ -341,8 +349,13 @@ impl SquareBitMatrix {
reader.read(&mut expanded);

// Now, write into the correct column
for i in 0..rows {
out.0[i].0[j / 64] |= u64::from((expanded[i / 8] >> (i % 8)) & 1) << (j % 64);
let j_word = j / 64;
let j_bit = j % 64;
for (i, out_row) in out.0.iter_mut().enumerate() {
let byte = expanded.get(i / 8).copied().unwrap_or(0);
if let Some(word) = out_row.0.get_mut(j_word) {
*word |= u64::from((byte >> (i % 8)) & 1) << j_bit;
}
}
}
out
Expand Down
Loading
Loading