diff --git a/src/circuit/sloth.rs b/src/circuit/sloth.rs index 5e463d1c..4a1f460a 100644 --- a/src/circuit/sloth.rs +++ b/src/circuit/sloth.rs @@ -77,10 +77,10 @@ mod tests { for _ in 0..10 { let key: Fr = rng.gen(); let plaintext: Fr = rng.gen(); - let ciphertext = sloth::encode::(&key, &plaintext, 10); + let ciphertext = sloth::encode_element::(&key, &plaintext, 10); // Vanilla - let decrypted = sloth::decode::(&key, &ciphertext, 10); + let decrypted = sloth::decode_element::(&key, &ciphertext, 10); let mut cs = TestConstraintSystem::::new(); let out = decode(cs.namespace(|| "sloth"), &key, &ciphertext, 10).unwrap(); @@ -98,9 +98,9 @@ mod tests { let key_bad: Fr = rng.gen(); let plaintext: Fr = rng.gen(); - let ciphertext = sloth::encode::(&key, &plaintext, 10); + let ciphertext = sloth::encode_element::(&key, &plaintext, 10); - let decrypted = sloth::decode::(&key, &ciphertext, 10); + let decrypted = sloth::decode_element::(&key, &ciphertext, 10); let mut cs = TestConstraintSystem::::new(); let out = decode(cs.namespace(|| "sloth"), &key_bad, &ciphertext, 10).unwrap(); @@ -117,9 +117,9 @@ mod tests { let key: Fr = rng.gen(); let plaintext: Fr = rng.gen(); - let ciphertext = sloth::encode::(&key, &plaintext, 10); + let ciphertext = sloth::encode_element::(&key, &plaintext, 10); - let decrypted = sloth::decode::(&key, &ciphertext, 10); + let decrypted = sloth::decode_element::(&key, &ciphertext, 10); let mut cs = TestConstraintSystem::::new(); let out9 = decode(cs.namespace(|| "sloth 9"), &key, &ciphertext, 9).unwrap(); diff --git a/src/crypto/sloth.rs b/src/crypto/sloth.rs index b0c73c0e..748717fd 100644 --- a/src/crypto/sloth.rs +++ b/src/crypto/sloth.rs @@ -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. @@ -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 { + let frs = bytes_into_frs::(&mut plaintext)?; + Ok(frs + .into_iter() + .map(|fr| encode_element::(key, &fr, rounds)) + .flat_map(|fr| fr_into_bytes::(&fr)) + .collect()) +} + +pub fn decode<'a, E: Engine>(key: &E::Fr, mut ciphertext: &[u8], rounds: usize) -> Result> { + let frs = bytes_into_frs::(&mut ciphertext)?; + Ok(frs + .into_iter() + .map(|fr| decode_element::(key, &fr, rounds)) + .flat_map(|fr| fr_into_bytes::(&fr)) + .collect()) +} + /// Sloth based encoding. -pub fn encode(key: &E::Fr, plaintext: &E::Fr, rounds: usize) -> E::Fr { +pub fn encode_element(key: &E::Fr, plaintext: &E::Fr, rounds: usize) -> E::Fr { let mut ciphertext = *plaintext; for _ in 0..rounds { @@ -25,7 +45,7 @@ pub fn encode(key: &E::Fr, plaintext: &E::Fr, rounds: usize) -> E::Fr } /// Sloth based decoding. -pub fn decode(key: &E::Fr, ciphertext: &E::Fr, rounds: usize) -> E::Fr { +pub fn decode_element(key: &E::Fr, ciphertext: &E::Fr, rounds: usize) -> E::Fr { let mut plaintext = *ciphertext; for _ in 0..rounds { @@ -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::(&key, &plaintext, 10); - let decrypted = decode::(&key, &ciphertext, 10); + let ciphertext = encode_element::(&key, &plaintext, 10); + let decrypted = decode_element::(&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::(&key, &plaintext[..], 10).unwrap(); + let decrypted = decode::(&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::(&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::(&key, &plaintext, 10); - let decrypted = decode::(&key_fake, &ciphertext, 10); + let ciphertext = encode_element::(&key, &plaintext, 10); + let decrypted = decode_element::(&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::(&key, &plaintext, 10).unwrap(); + let decrypted = decode::(&key_fake, &ciphertext, 10).unwrap(); assert_ne!(plaintext, decrypted); } @@ -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::(&key, &plaintext, 10); - assert_eq!(decode::(&key, &ciphertext, 10), plaintext); + let ciphertext = encode_element::(&key, &plaintext, 10); + assert_eq!(decode_element::(&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::(&key, &plaintext, 10).unwrap(); + assert_eq!(&decode::(&key, &ciphertext, 10).unwrap(), plaintext); } } } diff --git a/src/fr32.rs b/src/fr32.rs new file mode 100644 index 00000000..2cdf6128 --- /dev/null +++ b/src/fr32.rs @@ -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; +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(bytes: &mut &Fr32) -> Result { + if bytes.len() != 32 { + return Err(BadFrBytesError); + } + let mut fr_repr = <<::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(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(bytes: &mut &[u8]) -> Result, BadFrBytesError> { + bytes + .chunks(32) + .map(|ref mut chunk| bytes_into_fr::(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(frs: &[E::Fr]) -> Fr32Vec { + frs.iter().flat_map(|fr| fr_into_bytes::(fr)).collect() +} + +#[cfg(test)] +mod tests { + use super::*; + fn bytes_fr_test(bytes: Fr32Ary, expect_success: bool) { + let mut b = &bytes[..]; + let fr_result = bytes_into_fr::(&mut b); + if expect_success { + let f = fr_result.unwrap(); + let b2 = fr_into_bytes::(&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::( + [ + 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::( + // 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::( + // 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::( + // 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::( + // 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(bytes: &Fr32) { + let mut bytes = bytes.clone(); + let frs = bytes_into_frs::(&mut bytes).unwrap(); + assert!(frs.len() == 3); + let bytes_back = frs_into_bytes::(&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::(&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::(&_short_bytes[..]); + } +} diff --git a/src/lib.rs b/src/lib.rs index f3fd92e4..cf1272ed 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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;