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

Update more of the spec to fips 203 #82

Merged
merged 12 commits into from
Sep 21, 2023
141 changes: 22 additions & 119 deletions specs/hacspec-lib/src/bit_vector.rs
franziskuskiefer marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,19 +1,11 @@
pub struct BitVector {
bits: Vec<u8>,
}

pub struct BitVectorChunks<'a> {
chunk_iterator: std::slice::Chunks<'a, u8>,
}

impl BitVectorChunks<'_> {
pub fn next(&mut self) -> Option<BitVector> {
self.chunk_iterator.next().map(|bits| BitVector {
bits: bits.to_vec(),
})
impl BitVector {
pub fn len(&self) -> usize {
return self.bits.len();
}
}

impl IntoIterator for BitVector {
type Item = u8;
type IntoIter = <Vec<u8> as IntoIterator>::IntoIter;
Expand All @@ -22,129 +14,40 @@ impl IntoIterator for BitVector {
self.bits.into_iter()
}
}

impl From<&[u8]> for BitVector {
fn from(bytes: &[u8]) -> Self {
let mut out = Vec::with_capacity(bytes.len() * 8);

for byte in bytes {
for j in 0..u8::BITS {
out.push((byte >> j) & 1);
}
impl BitVector {
pub fn new() -> Self {
Self {
bits: Vec::<u8>::new(),
}

Self { bits: out }
}
}

impl BitVector {
pub fn new(bits: Vec<u8>) -> Self {
for bit in &bits {
assert!(*bit == 0 || *bit == 1);
}
pub fn push(&mut self, value: u8) {
assert!(value == 0 || value == 1);

Self { bits }
self.bits.push(value);
}

pub fn chunks(&self, chunk_size: usize) -> BitVectorChunks {
BitVectorChunks {
chunk_iterator: self.bits.chunks(chunk_size),
}
}
}

pub trait LittleEndianBitStream {
fn nth_bit(&self, n: usize) -> u8;
fn iter(&self) -> LittleEndianBitStreamIter<'_>;
}

pub struct LittleEndianBitStreamIter<'a> {
bytes: &'a [u8],
bit: usize,
}

impl Iterator for LittleEndianBitStreamIter<'_> {
type Item = u8;

fn next(&mut self) -> Option<Self::Item> {
let byte_index = self.bit / 8;
if byte_index >= self.bytes.len() {
return None;
}

let out = self.bytes.nth_bit(self.bit);
self.bit += 1;

Some(out)
}
}

impl LittleEndianBitStream for &[u8] {
fn nth_bit(&self, n: usize) -> u8 {
let byte = n / 8;
let byte_bit = n % 8;
(self[byte] >> byte_bit) & 1
}

fn iter(&self) -> LittleEndianBitStreamIter<'_> {
LittleEndianBitStreamIter {
bytes: self,
bit: 0,
}
}
pub struct BitVectorChunks<'a> {
chunk_iterator: std::slice::Chunks<'a, u8>,
}

impl LittleEndianBitStream for Vec<u8> {
fn nth_bit(&self, n: usize) -> u8 {
self.as_slice().nth_bit(n)
}

fn iter(&self) -> LittleEndianBitStreamIter<'_> {
LittleEndianBitStreamIter {
bytes: self,
bit: 0,
}
impl BitVectorChunks<'_> {
pub fn next(&mut self) -> Option<BitVector> {
self.chunk_iterator.next().map(|bits| BitVector {
bits: bits.to_vec(),
})
}
}
impl<'a> Iterator for BitVectorChunks<'a> {
type Item = &'a [u8];

#[cfg(test)]
mod tests {
use crate::bit_vector::LittleEndianBitStream;

#[test]
fn bits() {
// 00000001 00000010 00000011 00000100 00000101 00000110 ...
// 1 2 3 4 5 6
let v = vec![1u8, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];

let mut n = 0;

assert_eq!(v.nth_bit(n), 1);
n += 1;

for n_ in n..8 {
assert_eq!(v.nth_bit(n_), 0);
}
n = 8;

assert_eq!(v.nth_bit(n), 0);
n += 1;

assert_eq!(v.nth_bit(n), 1);
n += 1;

for n_ in n..16 {
assert_eq!(v.nth_bit(n_), 0);
}
n = 16;

assert_eq!(v.nth_bit(n), 1);
n += 1;

assert_eq!(v.nth_bit(n), 1);

for n in v.iter() {
eprint!("{n}");
}
eprintln!();
fn next(&mut self) -> Option<Self::Item> {
self.chunk_iterator.next()
}
}
20 changes: 10 additions & 10 deletions specs/kyber/src/compress.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::parameters::{self, KyberFieldElement, KyberPolynomialRingElement};

/// According to the NIST FIPS 203 standard, compressing a polynomial
/// ring element is accomplished by `compress()`ing its constituent field
/// coefficients.
/// According to the NIST FIPS 203 standard (Page 10, Lines 536 - 539),
/// compressing a polynomial ring element is accomplished by `compress()`ing its
/// constituent field coefficients.
///
/// The NIST FIPS 203 standard can be found at
/// <https://csrc.nist.gov/pubs/fips/203/ipd>.
Expand All @@ -16,9 +16,9 @@ pub fn compress(
)
}

/// According to the NIST FIPS 203 standard, compressing a polynomial
/// ring element is accomplished by `decompress()`ing its constituent field
/// coefficients.
/// According to the NIST FIPS 203 standard (Page 10, Lines 536 - 539),
/// compressing a polynomial ring element is accomplished by `decompress()`ing
/// its constituent field coefficients.
///
/// The NIST FIPS 203 standard can be found at
/// <https://csrc.nist.gov/pubs/fips/203/ipd>.
Expand All @@ -32,8 +32,8 @@ pub fn decompress(
)
}

/// This function implements the `Compress` function defined on Page 18 of the
/// NIST FIPS 203 standard, which is defined as:
/// This function implements the `Compress` function specified in the NIST FIPS
/// 203 standard (Page 18, Expression 4.5), which is defined as:
///
/// ```plaintext
/// Compress_d: ℤq -> ℤ_{2ᵈ}
Expand Down Expand Up @@ -69,8 +69,8 @@ fn compress_d(fe: KyberFieldElement, to_bit_size: usize) -> KyberFieldElement {
(compressed % two_pow_bit_size).into()
}

/// This function implements the `Decompress` function defined on Page 18 of the
/// NIST FIPS 203 standard, which is defined as:
/// This function implements the `Deompress` function specified in the NIST FIPS
/// 203 standard (Page 18, Expression 4.6), which is defined as:
///
/// ```plaintext
/// Decompress_d: ℤ_{2ᵈ} -> ℤq
Expand Down
39 changes: 19 additions & 20 deletions specs/kyber/src/ind_cpa.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use hacspec_lib::{

use crate::{
compress::{compress, decompress},
matrix::{multiply_matrix_transpose_by_column, multiply_column_by_row, transpose},
matrix::{multiply_column_by_row, multiply_matrix_by_column, transpose},
ntt::{ntt, ntt_inverse},
parameters::{
hash_functions::{G, H, PRF, XOF},
Expand All @@ -15,7 +15,7 @@ use crate::{
VECTOR_U_ENCODED_SIZE, VECTOR_V_COMPRESSION_FACTOR,
},
sampling::{sample_ntt, sample_poly_cbd},
serialize::{deserialize_little_endian, serialize_little_endian},
serialize::{byte_decode, byte_encode},
BadRejectionSamplingRandomnessError,
};

Expand Down Expand Up @@ -50,10 +50,10 @@ impl KeyPair {
}
}

fn encode_12(input: [KyberPolynomialRingElement; RANK]) -> Vec<u8> {
fn byte_encode_12(input: [KyberPolynomialRingElement; RANK]) -> Vec<u8> {
let mut out = Vec::new();
for re in input.into_iter() {
out.extend_from_slice(&serialize_little_endian(re, 12));
out.extend_from_slice(&byte_encode(12, re));
}

out
Expand All @@ -72,7 +72,7 @@ fn encode_12(input: [KyberPolynomialRingElement; RANK]) -> Vec<u8> {
/// Output: encryption key ekₚₖₑ ∈ 𝔹^{384k+32}.
/// Output: decryption key dkₚₖₑ ∈ 𝔹^{384k}.
///
/// d $← B
/// d ←$ B
/// (ρ,σ) ← G(d)
/// N ← 0
/// for (i ← 0; i < k; i++)
Expand Down Expand Up @@ -174,16 +174,16 @@ pub(crate) fn generate_keypair(
}

// t̂ ← Â◦ŝ + ê
let mut t_as_ntt = multiply_matrix_transpose_by_column(&A_as_ntt, &secret_as_ntt);
let mut t_as_ntt = multiply_matrix_by_column(&A_as_ntt, &secret_as_ntt);
for i in 0..t_as_ntt.len() {
t_as_ntt[i] = t_as_ntt[i] + error_as_ntt[i];
}

// ekₚₖₑ ← ByteEncode₁₂(t̂) ‖ ρ
let public_key_serialized = encode_12(t_as_ntt).concat(seed_for_A);
let public_key_serialized = byte_encode_12(t_as_ntt).concat(seed_for_A);

// dkₚₖₑ ← ByteEncode₁₂(ŝ)
let secret_key_serialized = encode_12(secret_as_ntt);
let secret_key_serialized = byte_encode_12(secret_as_ntt);

Ok(KeyPair::new(
secret_key_serialized.into_array(),
Expand All @@ -194,9 +194,9 @@ pub(crate) fn generate_keypair(
fn encode_and_compress_u(input: [KyberPolynomialRingElement; RANK]) -> Vec<u8> {
let mut out = Vec::new();
for re in input.into_iter() {
out.extend_from_slice(&serialize_little_endian(
compress(re, VECTOR_U_COMPRESSION_FACTOR),
out.extend_from_slice(&byte_encode(
VECTOR_U_COMPRESSION_FACTOR,
compress(re, VECTOR_U_COMPRESSION_FACTOR),
));
}

Expand Down Expand Up @@ -257,7 +257,7 @@ pub(crate) fn encrypt(
.chunks(BYTES_PER_RING_ELEMENT)
.enumerate()
{
t_as_ntt[i] = deserialize_little_endian(12, t_as_ntt_bytes);
t_as_ntt[i] = byte_decode(12, t_as_ntt_bytes);
}

// ρ ← ekₚₖₑ[384k: 384k + 32]
Expand Down Expand Up @@ -328,14 +328,13 @@ pub(crate) fn encrypt(

// u ← NTT-¹(Âᵀ ◦ r̂) + e₁
let A_as_ntt_transpose = transpose(&A_as_ntt);
let mut u = multiply_matrix_transpose_by_column(&A_as_ntt_transpose, &r_as_ntt)
.map(|re| ntt_inverse(re));
let mut u = multiply_matrix_by_column(&A_as_ntt_transpose, &r_as_ntt).map(|re| ntt_inverse(re));
for i in 0..u.len() {
u[i] = u[i] + error_1[i];
}

// μ ← Decompress₁(ByteDecode₁(m)))
let message_as_ring_element = decompress(deserialize_little_endian(1, &message), 1);
let message_as_ring_element = decompress(byte_decode(1, &message), 1);

// v ← NTT-¹(t̂ᵀ ◦ r̂) + e₂ + μ
let v = ntt_inverse(multiply_column_by_row(&t_as_ntt, &r_as_ntt))
Expand All @@ -346,9 +345,9 @@ pub(crate) fn encrypt(
let c1 = encode_and_compress_u(u);

// c₂ ← ByteEncode_{dᵥ}(Compress_{dᵥ}(v))
let c2 = serialize_little_endian(
compress(v, VECTOR_V_COMPRESSION_FACTOR),
let c2 = byte_encode(
VECTOR_V_COMPRESSION_FACTOR,
compress(v, VECTOR_V_COMPRESSION_FACTOR),
);

// return c ← (c₁ ‖ c₂)
Expand Down Expand Up @@ -392,14 +391,14 @@ pub(crate) fn decrypt(
.enumerate()
{
u[i] = decompress(
deserialize_little_endian(VECTOR_U_COMPRESSION_FACTOR, u_bytes),
byte_decode(VECTOR_U_COMPRESSION_FACTOR, u_bytes),
VECTOR_U_COMPRESSION_FACTOR,
);
}

// v ← Decompress_{dᵥ}(ByteDecode_{dᵥ}(c₂))
let v = decompress(
deserialize_little_endian(
byte_decode(
VECTOR_V_COMPRESSION_FACTOR,
&ciphertext[VECTOR_U_ENCODED_SIZE..],
),
Expand All @@ -409,7 +408,7 @@ pub(crate) fn decrypt(
// ŝ ← ByteDecode₁₂(dkₚₖₑ)
let mut secret_as_ntt = [KyberPolynomialRingElement::ZERO; RANK];
for (i, secret_bytes) in secret_key.chunks_exact(BYTES_PER_RING_ELEMENT).enumerate() {
secret_as_ntt[i] = deserialize_little_endian(12, secret_bytes);
secret_as_ntt[i] = byte_decode(12, secret_bytes);
}

// w ← v - NTT-¹(ŝᵀ ◦ NTT(u))
Expand All @@ -422,5 +421,5 @@ pub(crate) fn decrypt(
// m ← ByteEncode₁(Compress₁(w))
// return m
// FIXME: remove conversion
serialize_little_endian(compress(message, 1), 1).as_array()
byte_encode(1, compress(message, 1)).as_array()
}
Loading