Skip to content

Commit

Permalink
Merge pull request zcash#47 from filecoin-project/feat/fr32
Browse files Browse the repository at this point in the history
Add Fr32 and use in Sloth.
  • Loading branch information
porcuquine authored Jun 18, 2018
2 parents b4de7e5 + f4189be commit 1eec131
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 15 deletions.
12 changes: 6 additions & 6 deletions src/circuit/sloth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,10 @@ mod tests {
for _ in 0..10 {
let key: Fr = rng.gen();
let plaintext: Fr = rng.gen();
let ciphertext = sloth::encode::<Bls12>(&key, &plaintext, 10);
let ciphertext = sloth::encode_element::<Bls12>(&key, &plaintext, 10);

// Vanilla
let decrypted = sloth::decode::<Bls12>(&key, &ciphertext, 10);
let decrypted = sloth::decode_element::<Bls12>(&key, &ciphertext, 10);
let mut cs = TestConstraintSystem::<Bls12>::new();
let out = decode(cs.namespace(|| "sloth"), &key, &ciphertext, 10).unwrap();

Expand All @@ -98,9 +98,9 @@ mod tests {
let key_bad: Fr = rng.gen();
let plaintext: Fr = rng.gen();

let ciphertext = sloth::encode::<Bls12>(&key, &plaintext, 10);
let ciphertext = sloth::encode_element::<Bls12>(&key, &plaintext, 10);

let decrypted = sloth::decode::<Bls12>(&key, &ciphertext, 10);
let decrypted = sloth::decode_element::<Bls12>(&key, &ciphertext, 10);
let mut cs = TestConstraintSystem::<Bls12>::new();
let out = decode(cs.namespace(|| "sloth"), &key_bad, &ciphertext, 10).unwrap();

Expand All @@ -117,9 +117,9 @@ mod tests {
let key: Fr = rng.gen();
let plaintext: Fr = rng.gen();

let ciphertext = sloth::encode::<Bls12>(&key, &plaintext, 10);
let ciphertext = sloth::encode_element::<Bls12>(&key, &plaintext, 10);

let decrypted = sloth::decode::<Bls12>(&key, &ciphertext, 10);
let decrypted = sloth::decode_element::<Bls12>(&key, &ciphertext, 10);

let mut cs = TestConstraintSystem::<Bls12>::new();
let out9 = decode(cs.namespace(|| "sloth 9"), &key, &ciphertext, 9).unwrap();
Expand Down
81 changes: 72 additions & 9 deletions src/crypto/sloth.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use error::Result;
use fr32::{bytes_into_frs, fr_into_bytes, Fr32, Fr32Vec};
use pairing::{Engine, Field};

/// The `v` constant for sloth.
Expand All @@ -12,8 +14,26 @@ const SLOTH_V: [u64; 4] = [
/// The number five, as an array so we can use it in `pow`.
const FIVE: [u64; 1] = [5];

pub fn encode<'a, E: Engine>(key: &E::Fr, mut plaintext: &[u8], rounds: usize) -> Result<Fr32Vec> {
let frs = bytes_into_frs::<E>(&mut plaintext)?;
Ok(frs
.into_iter()
.map(|fr| encode_element::<E>(key, &fr, rounds))
.flat_map(|fr| fr_into_bytes::<E>(&fr))
.collect())
}

pub fn decode<'a, E: Engine>(key: &E::Fr, mut ciphertext: &[u8], rounds: usize) -> Result<Vec<u8>> {
let frs = bytes_into_frs::<E>(&mut ciphertext)?;
Ok(frs
.into_iter()
.map(|fr| decode_element::<E>(key, &fr, rounds))
.flat_map(|fr| fr_into_bytes::<E>(&fr))
.collect())
}

/// Sloth based encoding.
pub fn encode<E: Engine>(key: &E::Fr, plaintext: &E::Fr, rounds: usize) -> E::Fr {
pub fn encode_element<E: Engine>(key: &E::Fr, plaintext: &E::Fr, rounds: usize) -> E::Fr {
let mut ciphertext = *plaintext;

for _ in 0..rounds {
Expand All @@ -25,7 +45,7 @@ pub fn encode<E: Engine>(key: &E::Fr, plaintext: &E::Fr, rounds: usize) -> E::Fr
}

/// Sloth based decoding.
pub fn decode<E: Engine>(key: &E::Fr, ciphertext: &E::Fr, rounds: usize) -> E::Fr {
pub fn decode_element<E: Engine>(key: &E::Fr, ciphertext: &E::Fr, rounds: usize) -> E::Fr {
let mut plaintext = *ciphertext;

for _ in 0..rounds {
Expand Down Expand Up @@ -54,19 +74,47 @@ mod tests {
fn sloth_bls_12() {
let key = Fr::from_str("11111111").unwrap();
let plaintext = Fr::from_str("123456789").unwrap();
let ciphertext = encode::<Bls12>(&key, &plaintext, 10);
let decrypted = decode::<Bls12>(&key, &ciphertext, 10);
let ciphertext = encode_element::<Bls12>(&key, &plaintext, 10);
let decrypted = decode_element::<Bls12>(&key, &ciphertext, 10);
assert_eq!(plaintext, decrypted);
assert_ne!(plaintext, ciphertext);
}

#[test]
fn test_sloth_bls_12_bytes() {
let key = Fr::from_str("11111111").unwrap();
let plaintext = b"Exactly thirty-two bytes in all.".to_vec();
let ciphertext = encode::<Bls12>(&key, &plaintext[..], 10).unwrap();
let decrypted = decode::<Bls12>(&key, &ciphertext, 10).unwrap();
assert_eq!(plaintext, decrypted);
assert_ne!(plaintext, ciphertext);
}

#[test]
fn test_sloth_bls_12_bytes_bad() {
let key = Fr::from_str("11111111").unwrap();
let plaintext = b"Not quite the right number of bytes.".to_vec();
let ciphertext = encode::<Bls12>(&key, &plaintext[..], 10);
assert!(ciphertext.is_err());
}

#[test]
fn sloth_bls_12_fake() {
let key = Fr::from_str("11111111").unwrap();
let key_fake = Fr::from_str("11111112").unwrap();
let plaintext = Fr::from_str("123456789").unwrap();
let ciphertext = encode::<Bls12>(&key, &plaintext, 10);
let decrypted = decode::<Bls12>(&key_fake, &ciphertext, 10);
let ciphertext = encode_element::<Bls12>(&key, &plaintext, 10);
let decrypted = decode_element::<Bls12>(&key_fake, &ciphertext, 10);
assert_ne!(plaintext, decrypted);
}

#[test]
fn test_sloth_bls_12_fake_bytes() {
let key = Fr::from_str("11111111").unwrap();
let key_fake = Fr::from_str("22222222").unwrap();
let plaintext = b"Exactly thirty-two bytes in all.".to_vec();
let ciphertext = encode::<Bls12>(&key, &plaintext, 10).unwrap();
let decrypted = decode::<Bls12>(&key_fake, &ciphertext, 10).unwrap();
assert_ne!(plaintext, decrypted);
}

Expand All @@ -75,12 +123,27 @@ mod tests {
Fr::from_repr(FrRepr([a, b, c, d])).unwrap()
}
}

proptest!{
#[test]
fn sloth_bls_roundtrip(key in arb_fr(), plaintext in arb_fr()) {
let ciphertext = encode::<Bls12>(&key, &plaintext, 10);
assert_eq!(decode::<Bls12>(&key, &ciphertext, 10), plaintext);
let ciphertext = encode_element::<Bls12>(&key, &plaintext, 10);
assert_eq!(decode_element::<Bls12>(&key, &ciphertext, 10), plaintext);
}
}
prop_compose! {
fn arb_fr32()(body in 0..0b11111111u8, end in 0..0b00111111u8 // Last byte must have two-bits padding in most-significant bit.
) -> Fr32Vec {
[body, body, body, body, body, body, body, body, body, body, body, body, body, body, body, body,
body, body, body, body, body, body, body, body, body, body, body, body, body, body, body, end,
] .to_vec()
}
}

proptest!{
#[test]
fn sloth_bls_bytes_roundtrip(key in arb_fr(), ref plaintext in arb_fr32()) {
let ciphertext = encode::<Bls12>(&key, &plaintext, 10).unwrap();
assert_eq!(&decode::<Bls12>(&key, &ciphertext, 10).unwrap(), plaintext);
}
}
}
121 changes: 121 additions & 0 deletions src/fr32.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
use pairing::bls12_381::Bls12;
use pairing::{Engine, PrimeField, PrimeFieldRepr};

#[derive(Fail, Debug)]
#[fail(display = "Bytes could not be converted to Fr")]
pub struct BadFrBytesError;

pub type Fr32 = [u8];
pub type Fr32Vec = Vec<u8>;
pub type Fr32Ary = [u8; 32];

// Takes a slice of bytes and returns an Fr if byte slice is exactly 32 bytes and does not overflow.
// Otherwise, returns a BadFrBytesError.
pub fn bytes_into_fr<E: Engine>(bytes: &mut &Fr32) -> Result<E::Fr, BadFrBytesError> {
if bytes.len() != 32 {
return Err(BadFrBytesError);
}
let mut fr_repr = <<<E as Engine>::Fr as PrimeField>::Repr as Default>::default();
fr_repr.read_le(bytes).map_err(|_| BadFrBytesError)?;

E::Fr::from_repr(fr_repr).map_err(|_| BadFrBytesError)
}

// Takes an Fr and returns a vector of exactly 32 bytes guaranteed to contain a valid Fr.
pub fn fr_into_bytes<E: Engine>(fr: &E::Fr) -> Fr32Vec {
let mut out = Vec::new();
fr.into_repr().write_le(&mut out).unwrap();
out
}

// Takes a slice of bytes and returns a vector of Fr -- or an error if either bytes is not a multiple of 32 bytes
// or any 32-byte chunk overflows and does not contain a valid Fr.
pub fn bytes_into_frs<E: Engine>(bytes: &mut &[u8]) -> Result<Vec<E::Fr>, BadFrBytesError> {
bytes
.chunks(32)
.map(|ref mut chunk| bytes_into_fr::<E>(chunk))
.collect()
}

// Takes a slice of Frs and returns a vector of bytes, guaranteed to have a size which is a multiple of 32,
// with every 32-byte chunk representing a valid Fr.
pub fn frs_into_bytes<E: Engine>(frs: &[E::Fr]) -> Fr32Vec {
frs.iter().flat_map(|fr| fr_into_bytes::<E>(fr)).collect()
}

#[cfg(test)]
mod tests {
use super::*;
fn bytes_fr_test<E: Engine>(bytes: Fr32Ary, expect_success: bool) {
let mut b = &bytes[..];
let fr_result = bytes_into_fr::<E>(&mut b);
if expect_success {
let f = fr_result.unwrap();
let b2 = fr_into_bytes::<E>(&f);

assert_eq!(bytes.to_vec(), b2);
} else {
assert!(fr_result.is_err(), "expected a decoding error")
}
}
#[test]
fn test_bytes_into_fr_into_bytes() {
bytes_fr_test::<Bls12>(
[
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22,
23, 24, 25, 26, 27, 28, 29, 30, 31,
],
true,
);
bytes_fr_test::<Bls12>(
// Some bytes fail because they are not in the field.
[
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 115,
],
false,
);
bytes_fr_test::<Bls12>(
// This is okay.
[
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 114,
],
true,
);
bytes_fr_test::<Bls12>(
// So is this.
[
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 236, 115,
],
true,
);
bytes_fr_test::<Bls12>(
// But not this.
[
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 237, 115,
],
false,
);
}

fn bytes_into_frs_into_bytes_test<E: Engine>(bytes: &Fr32) {
let mut bytes = bytes.clone();
let frs = bytes_into_frs::<E>(&mut bytes).unwrap();
assert!(frs.len() == 3);
let bytes_back = frs_into_bytes::<E>(&frs);
assert!(bytes.to_vec() == bytes_back);
}

#[test]
fn test_bytes_into_frs_into_bytes() {
let bytes = b"012345678901234567890123456789--012345678901234567890123456789--012345678901234567890123456789--";
bytes_into_frs_into_bytes_test::<Bls12>(&bytes[..]);

let _short_bytes = b"012345678901234567890123456789--01234567890123456789";
// This will panic because _short_bytes is not a multiple of 32 bytes.
// bytes_into_frs_into_bytes_test::<Bls12>(&_short_bytes[..]);
}
}
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ pub mod crypto;
pub mod drgporep;
pub mod drgraph;
pub mod error;
pub mod fr32;
pub mod hasher;
pub mod layered_drgporep;
pub mod merklepor;
Expand Down

0 comments on commit 1eec131

Please sign in to comment.