sha256.rs 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. // Say we want to write a circuit that proves we know the preimage to some hash computed
  2. // using SHA-256d (calling SHA-256 twice). The preimage must have a fixed length known in
  3. // advance (because the circuit parameters will depend on it), but can otherwise have any value.
  4. // We take the following strategy:
  5. //
  6. // * Witness each bit of the preimage.
  7. // * Compute hash = SHA-256d(preimage) inside the circuit.
  8. // * Expose hash as a public input using multiscalar packing.
  9. //
  10. use bellman::{
  11. gadgets::{
  12. boolean::{AllocatedBit, Boolean},
  13. multipack,
  14. sha256::sha256,
  15. },
  16. groth16, Circuit, ConstraintSystem, SynthesisError,
  17. };
  18. use bls12_381::Bls12;
  19. use ff::PrimeField;
  20. use rand::rngs::OsRng;
  21. use sha2::{Digest, Sha256};
  22. /// Our own SHA-256d gadget. Input and output are in little-endian bit order.
  23. fn sha256d<Scalar: PrimeField, CS: ConstraintSystem<Scalar>>(
  24. mut cs: CS,
  25. data: &[Boolean],
  26. ) -> Result<Vec<Boolean>, SynthesisError> {
  27. // Flip endianness of each input byte
  28. // NOTE: data is a vec of Bool so it is iterating over 8 'bits' at a time
  29. // This is needed because Rust sha256 and ZC sha256 have different endianness.
  30. let input: Vec<_> = data
  31. .chunks(8)
  32. .map(|c| c.iter().rev())
  33. .flatten()
  34. .cloned()
  35. .collect();
  36. let mid = sha256(cs.namespace(|| "SHA-256(input)"), &input)?;
  37. let res = sha256(cs.namespace(|| "SHA-256(mid)"), &mid)?;
  38. // Flip endianness of each output byte
  39. Ok(res
  40. .chunks(8)
  41. .map(|c| c.iter().rev())
  42. .flatten()
  43. .cloned()
  44. .collect())
  45. }
  46. struct MyCircuit {
  47. /// The input to SHA-256d we are proving that we know. Set to `None` when we
  48. /// are verifying a proof (and do not have the witness data).
  49. preimage: Option<[u8; 80]>,
  50. }
  51. impl<Scalar: PrimeField> Circuit<Scalar> for MyCircuit {
  52. fn synthesize<CS: ConstraintSystem<Scalar>>(self, cs: &mut CS) -> Result<(), SynthesisError> {
  53. // Compute the values for the bits of the preimage. If we are verifying a proof,
  54. // we still need to create the same constraints, so we return an equivalent-size
  55. // Vec of None (indicating that the value of each bit is unknown).
  56. let bit_values = if let Some(preimage) = self.preimage {
  57. preimage
  58. .iter()
  59. .map(|byte| (0..8).map(move |i| (byte >> i) & 1u8 == 1u8))
  60. .flatten()
  61. .map(|b| Some(b))
  62. .collect()
  63. } else {
  64. vec![None; 80 * 8]
  65. };
  66. assert_eq!(bit_values.len(), 80 * 8);
  67. // Witness the bits of the preimage.
  68. let preimage_bits = bit_values
  69. .into_iter()
  70. .enumerate()
  71. // Allocate each bit.
  72. .map(|(i, b)| AllocatedBit::alloc(cs.namespace(|| format!("preimage bit {}", i)), b))
  73. // Convert the AllocatedBits into Booleans (required for the sha256 gadget).
  74. .map(|b| b.map(Boolean::from))
  75. .collect::<Result<Vec<_>, _>>()?;
  76. // Compute hash = SHA-256d(preimage).
  77. let hash = sha256d(cs.namespace(|| "SHA-256d(preimage)"), &preimage_bits)?;
  78. // Expose the vector of 32 boolean variables as compact public inputs.
  79. multipack::pack_into_inputs(cs.namespace(|| "pack hash"), &hash)
  80. }
  81. }
  82. fn main() {
  83. use std::time::Instant;
  84. let start = Instant::now();
  85. println!("Starting...");
  86. // Create parameters for our circuit. In a production deployment these would
  87. // be generated securely using a multiparty computation.
  88. let params = {
  89. let c = MyCircuit { preimage: None };
  90. groth16::generate_random_parameters::<Bls12, _, _>(c, &mut OsRng).unwrap()
  91. };
  92. println!("Generated random params. [{:?}]", start.elapsed());
  93. let start = Instant::now();
  94. // Prepare the verification key (for proof verification).
  95. let pvk = groth16::prepare_verifying_key(&params.vk);
  96. println!("Prepared verify key [{:?}]", start.elapsed());
  97. let start = Instant::now();
  98. // Pick a preimage and compute its hash.
  99. let preimage = [42; 80];
  100. let hash = Sha256::digest(&Sha256::digest(&preimage));
  101. println!(
  102. "Computed sha256(sha256(preimage)) witness data [{:?}]",
  103. start.elapsed()
  104. );
  105. // Create an instance of our circuit (with the preimage as a witness).
  106. let c = MyCircuit {
  107. preimage: Some(preimage),
  108. };
  109. let start = Instant::now();
  110. // Create a Groth16 proof with our parameters.
  111. let proof = groth16::create_random_proof(c, &params, &mut OsRng).unwrap();
  112. println!("Generated random proof [{:?}]", start.elapsed());
  113. let start = Instant::now();
  114. // Pack the hash as inputs for proof verification.
  115. let hash_bits = multipack::bytes_to_bits_le(&hash);
  116. let inputs = multipack::compute_multipacking(&hash_bits);
  117. println!("Packed data and verifying proof... [{:?}]", start.elapsed());
  118. let start = Instant::now();
  119. // Check the proof!
  120. assert!(groth16::verify_proof(&pvk, &proof, &inputs).is_ok());
  121. println!("Done! [{:?}]", start.elapsed());
  122. }