From 3d380d6f583063fe36185862f675292618621fb8 Mon Sep 17 00:00:00 2001 From: Michael Elliot Date: Wed, 25 Oct 2023 17:09:21 +0800 Subject: [PATCH] add project files --- .github/workflows/test.yml | 25 +++++ LICENSE-MIT | 21 +++++ README.md | 79 ++++++++++++++++ crates/noir-sha2/Nargo.toml | 6 ++ crates/noir-sha2/README.md | 27 ++++++ crates/noir-sha2/src/lib.nr | 180 ++++++++++++++++++++++++++++++++++++ example/Nargo.toml | 8 ++ example/Prover.toml | 3 + example/Verifier.toml | 1 + example/src/main.nr | 26 ++++++ 10 files changed, 376 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 LICENSE-MIT create mode 100644 README.md create mode 100644 crates/noir-sha2/Nargo.toml create mode 100644 crates/noir-sha2/README.md create mode 100644 crates/noir-sha2/src/lib.nr create mode 100644 example/Nargo.toml create mode 100644 example/Prover.toml create mode 100644 example/Verifier.toml create mode 100644 example/src/main.nr diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..c5a1ef0 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,25 @@ +name: Tests 🌌 + +on: + push: + pull_request: + branches: [main] + +env: + CARGO_TERM_COLOR: always + +jobs: + unit: + runs-on: ubuntu-latest + steps: + - name: Checkout sources + uses: actions/checkout@v2 + + - name: Install Nargo + uses: noir-lang/noirup@v0.1.2 + with: + toolchain: v0.17.0 + + - name: Run nargo test + run: | + cd crates/noir-sha2/ && nargo test diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..c13bee5 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Michael Elliot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..5b52d94 --- /dev/null +++ b/README.md @@ -0,0 +1,79 @@ +# Noir SHA-2 + +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![Nargo Test 🌌](https://github.com/michaelelliot/noir-sha2/actions/workflows/test.yml/badge.svg)](https://github.com/michaelelliot/noir-sha2/actions/workflows/test.yml) + +This library contains a Noir implementation of the SHA-2 hashing algorithm. + +## Usage + +In your `Nargo.toml` file, add the following dependency: + +```toml +[dependencies] +sha2 = { tag = "v0.0.1", git = "https://github.com/michaelelliot/noir-sha2", directory = "crates/noir-sha2" } +``` + +Then use it in your Noir project like this: + +```rust +use dep::sha2::{sha2}; + +fn main(input: [u8; 64], input_len: u16, hash: pub [u8; 32]) { + // Generate SHA-2 hash digest of input + let compare_hash = sha2(input, input_len); + assert(hash == compare_hash); +} +``` + +*NOTE:* The `input` parameter must be a `u8` byte array with a length that's a multiple of 64, such as 64, 128, 192, or 256 etc. +The rest of the byte array can be zero-padded (`0x00`) as shown in the example below, with the `input_len` parameter specifying the number of initial bytes from the `input` to be used for calculating the digest. + +Here's an example unit test for the `main` entrypoint above: + +```rust +#[test] +fn test_main() { + // Hal Finney was a cypherpunk pioneer + let test_msg: [u8; 64] = [ + 0x48, 0x61, 0x6c, 0x20, 0x46, 0x69, 0x6e, 0x6e, + 0x65, 0x79, 0x20, 0x77, 0x61, 0x73, 0x20, 0x61, + 0x20, 0x63, 0x79, 0x70, 0x68, 0x65, 0x72, 0x70, + 0x75, 0x6e, 0x6b, 0x20, 0x70, 0x69, 0x6f, 0x6e, + 0x65, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let test_hash: [u8; 32] = [ + 0xd4, 0x5d, 0xa9, 0xcf, 0x6f, 0xc0, 0xe0, 0x56, 0x7e, 0xa2, 0x7d, 0xf1, 0xbb, 0x17, 0xfb, 0x0a, + 0x28, 0x10, 0x29, 0xca, 0x23, 0x17, 0x4f, 0xd0, 0x64, 0xad, 0xc4, 0x9a, 0x98, 0x7f, 0xd9, 0xff]; + main(test_msg, 35, test_hash); +} +``` + +## Example + +Noir example source code: [`./example/src/main.nr`](./example/src/main.nr) + +## License + +MIT License + +Copyright (c) 2023 Michael Elliot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/noir-sha2/Nargo.toml b/crates/noir-sha2/Nargo.toml new file mode 100644 index 0000000..86584d6 --- /dev/null +++ b/crates/noir-sha2/Nargo.toml @@ -0,0 +1,6 @@ +[package] +name = "sha2" +type = "lib" +authors = ["@michaelelliot"] +compiler_version = "0.10.5" +license = "MIT" diff --git a/crates/noir-sha2/README.md b/crates/noir-sha2/README.md new file mode 100644 index 0000000..ffd554a --- /dev/null +++ b/crates/noir-sha2/README.md @@ -0,0 +1,27 @@ +# Noir SHA-2 + +This library contains a Noir implementation of the SHA-2 hashing algorithm. + +## License + +MIT License + +Copyright (c) 2023 Michael Elliot + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/noir-sha2/src/lib.nr b/crates/noir-sha2/src/lib.nr new file mode 100644 index 0000000..5b105cf --- /dev/null +++ b/crates/noir-sha2/src/lib.nr @@ -0,0 +1,180 @@ +pub fn sha2(input: [u8; M], input_len: u16) -> [u8; 32] { + let k: [u32; 64] = [ + 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 + ]; + let mut h0: u32 = 0x6A09E667; + let mut h1: u32 = 0xBB67AE85; + let mut h2: u32 = 0x3C6EF372; + let mut h3: u32 = 0xA54FF53A; + let mut h4: u32 = 0x510E527F; + let mut h5: u32 = 0x9B05688C; + let mut h6: u32 = 0x1F83D9AB; + let mut h7: u32 = 0x5BE0CD19; + + let mut msg: [u8; M] = [0; M]; + for i in 0..M { msg[i] = input[i]; } + msg[input_len] = 0x80; + + let msg_length_in_bits_as_be_bytes: [u8; 8] = u64_to_u8(input_len as u64 * 8); + let mut rem: u16 = fast_div_mod_rem(input_len + 1, 64); + rem = if rem <= 56 { + 56 as u16 - rem + } else { + 56 as u16 + (64 as u16 - rem) + }; + let msg_padding_end: u16 = input_len + 1 + rem; + for i in 0..8 { + msg[msg_padding_end+i as u16] = msg_length_in_bits_as_be_bytes[i]; + } + + let num_chunks: u16 = (msg_padding_end / 64) + 1; + let num_chunks_max: Field = (M/64); + for chunk_idx in 0..num_chunks_max { + if chunk_idx as u16 < num_chunks as u16 { + let start_idx = chunk_idx * 64; + let mut chunk: [u8; 64] = [0; 64]; + for j in 0..64 { + chunk[j] = msg[start_idx as Field + j as Field]; + } + let mut w: [u32; 64] = [0; 64]; + for i in 0..16 { + w[i] = bytes_to_u32([ + chunk[ i * 4], + chunk[(i * 4) + 1], + chunk[(i * 4) + 2], + chunk[(i * 4) + 3]]); + } + for i in 16..64 { + w[i] = sigma1(w[i - 2]) + w[i - 7] + sigma0(w[i - 15]) + w[i - 16]; + } + + let (mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut h) = (h0, h1, h2, h3, h4, h5, h6, h7); + for t in 0..64 { + let temp1: u32 = h + big_sigma1(e) + ch(e, f, g) + k[t] + w[t]; + let temp2: u32 = big_sigma0(a) + maj(a, b, c); + h = g; + g = f; + f = e; + e = d + temp1; + d = c; + c = b; + b = a; + a = temp1 + temp2; + } + h0 += a; + h1 += b; + h2 += c; + h3 += d; + h4 += e; + h5 += f; + h6 += g; + h7 += h; + } + } + + let h0_bytes = u32_to_u8(h0); + let h1_bytes = u32_to_u8(h1); + let h2_bytes = u32_to_u8(h2); + let h3_bytes = u32_to_u8(h3); + let h4_bytes = u32_to_u8(h4); + let h5_bytes = u32_to_u8(h5); + let h6_bytes = u32_to_u8(h6); + let h7_bytes = u32_to_u8(h7); + + let mut result: [u8; 32] = [0; 32]; + for i in 0..4 { result[i] = h0_bytes[i]; } + for i in 0..4 { result[4+i] = h1_bytes[i]; } + for i in 0..4 { result[8+i] = h2_bytes[i]; } + for i in 0..4 { result[12+i] = h3_bytes[i]; } + for i in 0..4 { result[16+i] = h4_bytes[i]; } + for i in 0..4 { result[20+i] = h5_bytes[i]; } + for i in 0..4 { result[24+i] = h6_bytes[i]; } + for i in 0..4 { result[28+i] = h7_bytes[i]; } + + result +} + +fn fast_div_mod_rem(dividend: u16, divisor: u16) -> u16 { + if dividend == divisor { 0 } + else if dividend < divisor { dividend } + else { + let quot: u16 = dividend / divisor; + let rem: u16 = dividend - (divisor * quot); + rem + } +} + +fn bytes_to_u32(bytes: [u8; 4]) -> u32 { + bytes[0] as u32 << 24 | bytes[1] as u32 << 16 | bytes[2] as u32 << 8 | bytes[3] as u32 +} + +fn u32_to_u8(num: u32) -> [u8; 4] { + let mut result: [u8; 4] = [0; 4]; + for i in 0..4 { + result[i] = (num >> (24 - (i * 8))) as u8; + } + result +} + +fn u64_to_u8(num: u64) -> [u8; 8] { + let mut result: [u8; 8] = [0; 8]; + for i in 0..8 { + result[i] = (num >> (56 - (i * 8))) as u8; + } + result +} + +fn sigma0(x: u32) -> u32 { + right_rotate(x, 7) ^ right_rotate(x, 18) ^ (x >> 3) +} + +fn sigma1(x: u32) -> u32 { + right_rotate(x, 17) ^ right_rotate(x, 19) ^ (x >> 10) +} + +fn big_sigma0(x: u32) -> u32 { + right_rotate(x, 2) ^ right_rotate(x, 13) ^ right_rotate(x, 22) +} + +fn big_sigma1(x: u32) -> u32 { + right_rotate(x, 6) ^ right_rotate(x, 11) ^ right_rotate(x, 25) +} + +fn right_rotate(n: u32, d: u32) -> u32 { + (n >> d) | (n << (32 - d)) & 0xFFFFFFFF +} + +fn maj(a: u32, b: u32, c: u32) -> u32 { + (a & b) ^ (a & c) ^ (b & c) +} + +fn ch(a: u32, b: u32, c: u32) -> u32 { + (a & b) ^ ((!a) & c) +} + +#[test] +fn test_sha2() { + // Hal Finney was a cypherpunk pioneer + let test_msg: [u8; 64] = [ + 0x48, 0x61, 0x6c, 0x20, 0x46, 0x69, 0x6e, 0x6e, + 0x65, 0x79, 0x20, 0x77, 0x61, 0x73, 0x20, 0x61, + 0x20, 0x63, 0x79, 0x70, 0x68, 0x65, 0x72, 0x70, + 0x75, 0x6e, 0x6b, 0x20, 0x70, 0x69, 0x6f, 0x6e, + 0x65, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let test_hash: [u8; 32] = [ + 0xd4, 0x5d, 0xa9, 0xcf, 0x6f, 0xc0, 0xe0, 0x56, 0x7e, 0xa2, 0x7d, 0xf1, 0xbb, 0x17, 0xfb, 0x0a, + 0x28, 0x10, 0x29, 0xca, 0x23, 0x17, 0x4f, 0xd0, 0x64, 0xad, 0xc4, 0x9a, 0x98, 0x7f, 0xd9, 0xff]; + + let hash = sha2(test_msg, 35); + assert(hash == test_hash); +} diff --git a/example/Nargo.toml b/example/Nargo.toml new file mode 100644 index 0000000..a950bb0 --- /dev/null +++ b/example/Nargo.toml @@ -0,0 +1,8 @@ +[package] +name = "sha2_example" +type = "bin" +authors = ["@michaelelliot"] +compiler_version = "0.10.5" + +[dependencies] +sha2 = { path = "../crates/noir-sha2" } diff --git a/example/Prover.toml b/example/Prover.toml new file mode 100644 index 0000000..8fe3755 --- /dev/null +++ b/example/Prover.toml @@ -0,0 +1,3 @@ +input = ["72", "97", "108", "32", "70", "105", "110", "110", "101", "121", "32", "119", "97", "115", "32", "97", "32", "99", "121", "112", "104", "101", "114", "112", "117", "110", "107", "32", "112", "105", "111", "110", "101", "101", "114", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", "0"] +input_len = "35" +hash = ["212", "93", "169", "207", "111", "192", "224", "86", "126", "162", "125", "241", "187", "23", "251", "10", "40", "16", "41", "202", "35", "23", "79", "208", "100", "173", "196", "154", "152", "127", "217", "255"] diff --git a/example/Verifier.toml b/example/Verifier.toml new file mode 100644 index 0000000..2dc18dd --- /dev/null +++ b/example/Verifier.toml @@ -0,0 +1 @@ +hash = ["0x00000000000000000000000000000000000000000000000000000000000000d4", "0x000000000000000000000000000000000000000000000000000000000000005d", "0x00000000000000000000000000000000000000000000000000000000000000a9", "0x00000000000000000000000000000000000000000000000000000000000000cf", "0x000000000000000000000000000000000000000000000000000000000000006f", "0x00000000000000000000000000000000000000000000000000000000000000c0", "0x00000000000000000000000000000000000000000000000000000000000000e0", "0x0000000000000000000000000000000000000000000000000000000000000056", "0x000000000000000000000000000000000000000000000000000000000000007e", "0x00000000000000000000000000000000000000000000000000000000000000a2", "0x000000000000000000000000000000000000000000000000000000000000007d", "0x00000000000000000000000000000000000000000000000000000000000000f1", "0x00000000000000000000000000000000000000000000000000000000000000bb", "0x0000000000000000000000000000000000000000000000000000000000000017", "0x00000000000000000000000000000000000000000000000000000000000000fb", "0x000000000000000000000000000000000000000000000000000000000000000a", "0x0000000000000000000000000000000000000000000000000000000000000028", "0x0000000000000000000000000000000000000000000000000000000000000010", "0x0000000000000000000000000000000000000000000000000000000000000029", "0x00000000000000000000000000000000000000000000000000000000000000ca", "0x0000000000000000000000000000000000000000000000000000000000000023", "0x0000000000000000000000000000000000000000000000000000000000000017", "0x000000000000000000000000000000000000000000000000000000000000004f", "0x00000000000000000000000000000000000000000000000000000000000000d0", "0x0000000000000000000000000000000000000000000000000000000000000064", "0x00000000000000000000000000000000000000000000000000000000000000ad", "0x00000000000000000000000000000000000000000000000000000000000000c4", "0x000000000000000000000000000000000000000000000000000000000000009a", "0x0000000000000000000000000000000000000000000000000000000000000098", "0x000000000000000000000000000000000000000000000000000000000000007f", "0x00000000000000000000000000000000000000000000000000000000000000d9", "0x00000000000000000000000000000000000000000000000000000000000000ff"] diff --git a/example/src/main.nr b/example/src/main.nr new file mode 100644 index 0000000..06a0e2c --- /dev/null +++ b/example/src/main.nr @@ -0,0 +1,26 @@ +fn main( + input: [u8; 64], + input_len: u16, + hash: pub [u8; 32] +) { + let compare_hash = dep::sha2::sha2(input, input_len); + assert(hash == compare_hash); +} + +#[test] +fn test_main() { + // Hal Finney was a cypherpunk pioneer + let test_msg: [u8; 64] = [ + 0x48, 0x61, 0x6c, 0x20, 0x46, 0x69, 0x6e, 0x6e, + 0x65, 0x79, 0x20, 0x77, 0x61, 0x73, 0x20, 0x61, + 0x20, 0x63, 0x79, 0x70, 0x68, 0x65, 0x72, 0x70, + 0x75, 0x6e, 0x6b, 0x20, 0x70, 0x69, 0x6f, 0x6e, + 0x65, 0x65, 0x72, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; + let test_hash: [u8; 32] = [ + 0xd4, 0x5d, 0xa9, 0xcf, 0x6f, 0xc0, 0xe0, 0x56, 0x7e, 0xa2, 0x7d, 0xf1, 0xbb, 0x17, 0xfb, 0x0a, + 0x28, 0x10, 0x29, 0xca, 0x23, 0x17, 0x4f, 0xd0, 0x64, 0xad, 0xc4, 0x9a, 0x98, 0x7f, 0xd9, 0xff]; + main(test_msg, 35, test_hash); +}