Skip to content

Commit b8c9191

Browse files
authored
Merge pull request #22 from zkemail/TomAFrench-tf/add-ci
chore(ci): add a CI setup real
2 parents 24db6af + 21de652 commit b8c9191

12 files changed

+511
-351
lines changed

.github/workflows/test.yml

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
name: Noir tests
2+
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
8+
9+
jobs:
10+
test:
11+
name: Test on Nargo ${{matrix.toolchain}}
12+
runs-on: ubuntu-latest
13+
strategy:
14+
fail-fast: false
15+
matrix:
16+
toolchain: [0.36.0]
17+
steps:
18+
- name: Checkout sources
19+
uses: actions/checkout@v4
20+
21+
- name: Install Nargo
22+
uses: noir-lang/[email protected]
23+
with:
24+
toolchain: ${{ matrix.toolchain }}
25+
26+
- name: Run Noir tests
27+
working-directory: ./lib
28+
run: nargo test
29+
30+
format:
31+
runs-on: ubuntu-latest
32+
steps:
33+
- name: Checkout sources
34+
uses: actions/checkout@v4
35+
36+
- name: Install Nargo
37+
uses: noir-lang/[email protected]
38+
with:
39+
toolchain: 0.36.0
40+
41+
- name: Run formatter
42+
working-directory: ./lib
43+
run: nargo fmt --check
44+
45+
# This is a job which depends on all test jobs and reports the overall status.
46+
# This allows us to add/remove test jobs without having to update the required workflows.
47+
tests-end:
48+
name: Noir End
49+
runs-on: ubuntu-latest
50+
# We want this job to always run (even if the dependant jobs fail) as we want this job to fail rather than skipping.
51+
if: ${{ always() }}
52+
needs:
53+
- test
54+
- format
55+
56+
steps:
57+
- name: Report overall success
58+
run: |
59+
if [[ $FAIL == true ]]; then
60+
exit 1
61+
else
62+
exit 0
63+
fi
64+
env:
65+
# We treat any cancelled, skipped or failing jobs as a failure for the workflow as a whole.
66+
FAIL: ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'skipped') }}

lib/src/dkim.nr

+15-11
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,32 @@
1-
use dep::std::{collections::bounded_vec::BoundedVec, hash::{sha256_var, pedersen_hash}, panic::panic};
2-
use dep::bignum::{params::BigNumParams, RuntimeBigNum};
3-
use dep::rsa::{rsa::verify_sha256_pkcs1v15, types::{RBN1024, RBN2048}};
1+
use std::hash::{sha256_var, pedersen_hash};
2+
use bignum::{params::BigNumParams, RuntimeBigNum};
3+
use rsa::{rsa::verify_sha256_pkcs1v15, types::{RBN1024, RBN2048}};
44
use crate::{KEY_LIMBS_1024, KEY_LIMBS_2048, RSA_EXPONENT};
55

6-
76
pub struct RSAPubkey<let KEY_LIMBS: u32> {
87
modulus: [Field; KEY_LIMBS],
98
redc: [Field; KEY_LIMBS],
109
}
1110

1211
impl<let KEY_LIMBS: u32> RSAPubkey<KEY_LIMBS> {
13-
fn hash(self) -> Field {
12+
13+
pub fn new(modulus: [Field; KEY_LIMBS], redc: [Field; KEY_LIMBS]) -> Self {
14+
Self { modulus, redc }
15+
}
16+
17+
pub fn hash(self) -> Field {
1418
pedersen_hash(self.modulus)
1519
}
1620
}
1721

1822
impl RSAPubkey<KEY_LIMBS_1024> {
19-
fn verify_dkim_signature<let MAX_HEADER_LENGTH: u32>(
23+
pub fn verify_dkim_signature<let MAX_HEADER_LENGTH: u32>(
2024
self,
2125
header: BoundedVec<u8, MAX_HEADER_LENGTH>,
22-
signature: [Field; KEY_LIMBS_1024]
26+
signature: [Field; KEY_LIMBS_1024],
2327
) {
2428
// hash the header
25-
let header_hash = sha256_var(header.storage, header.len() as u64);
29+
let header_hash = sha256_var(header.storage(), header.len() as u64);
2630

2731
let params: BigNumParams<KEY_LIMBS_1024, 1024> =
2832
BigNumParams::new(false, self.modulus, self.redc);
@@ -35,13 +39,13 @@ impl RSAPubkey<KEY_LIMBS_1024> {
3539
}
3640

3741
impl RSAPubkey<KEY_LIMBS_2048> {
38-
fn verify_dkim_signature<let MAX_HEADER_LENGTH: u32>(
42+
pub fn verify_dkim_signature<let MAX_HEADER_LENGTH: u32>(
3943
self,
4044
header: BoundedVec<u8, MAX_HEADER_LENGTH>,
4145
signature: [Field; KEY_LIMBS_2048],
4246
) {
4347
// hash the header
44-
let header_hash = sha256_var(header.storage, header.len() as u64);
48+
let header_hash = sha256_var(header.storage(), header.len() as u64);
4549

4650
let params: BigNumParams<KEY_LIMBS_2048, 2048> =
4751
BigNumParams::new(false, self.modulus, self.redc);
@@ -51,4 +55,4 @@ impl RSAPubkey<KEY_LIMBS_2048> {
5155
// verify the DKIM signature over the header
5256
assert(verify_sha256_pkcs1v15(header_hash, signature, RSA_EXPONENT));
5357
}
54-
}
58+
}

lib/src/headers/body_hash.nr

+18-14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1-
use dep::base64::{BASE64_DECODER};
2-
use dep::std::collections::bounded_vec::BoundedVec;
3-
use crate::{Sequence, BODY_HASH_BASE64_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH, headers::constrain_header_field};
1+
use base64::BASE64_DECODER;
2+
use crate::{
3+
Sequence, BODY_HASH_BASE64_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH,
4+
headers::constrain_header_field,
5+
};
46

57
/**
68
* Constrained access to the body hash in the header
@@ -13,24 +15,26 @@ use crate::{Sequence, BODY_HASH_BASE64_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH, hea
1315
pub fn get_body_hash<let MAX_HEADER_LENGTH: u32>(
1416
header: BoundedVec<u8, MAX_HEADER_LENGTH>,
1517
dkim_header_field_sequence: Sequence,
16-
body_hash_index: u32
18+
body_hash_index: u32,
1719
) -> [u8; 32] {
1820
// constrain the access of dkim signature field
19-
let header_field_name: [u8; 14] = comptime {
20-
"dkim-signature".as_bytes()
21-
};
22-
constrain_header_field::<MAX_HEADER_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH, 14>(header, dkim_header_field_sequence, header_field_name);
21+
let header_field_name: [u8; 14] = comptime { "dkim-signature".as_bytes() };
22+
constrain_header_field::<MAX_HEADER_LENGTH, MAX_DKIM_HEADER_FIELD_LENGTH, 14>(
23+
header,
24+
dkim_header_field_sequence,
25+
header_field_name,
26+
);
2327
// constrain access to the body hash
2428
assert(
2529
body_hash_index > dkim_header_field_sequence.index
26-
& body_hash_index < dkim_header_field_sequence.end_index(), "Body hash index accessed outside of DKIM header field"
30+
& body_hash_index < dkim_header_field_sequence.end_index(),
31+
"Body hash index accessed outside of DKIM header field",
2732
);
28-
let bh_prefix: [u8; 3] = comptime {
29-
"bh=".as_bytes()
30-
};
33+
let bh_prefix: [u8; 3] = comptime { "bh=".as_bytes() };
3134
for i in 0..3 {
3235
assert(
33-
header.get_unchecked(body_hash_index - 3 + i) == bh_prefix[i], "No 'bh=' prefix found at asserted bh index"
36+
header.get_unchecked(body_hash_index - 3 + i) == bh_prefix[i],
37+
"No 'bh=' prefix found at asserted bh index",
3438
);
3539
}
3640
// get the body hash
@@ -46,7 +50,7 @@ pub fn get_body_hash<let MAX_HEADER_LENGTH: u32>(
4650
*/
4751
pub fn get_body_hash_unsafe<let MAX_HEADER_LENGTH: u32>(
4852
header: BoundedVec<u8, MAX_HEADER_LENGTH>,
49-
body_hash_index: u32
53+
body_hash_index: u32,
5054
) -> [u8; 32] {
5155
// get the body hash
5256
let mut body_hash_encoded: [u8; BODY_HASH_BASE64_LENGTH] = [0; BODY_HASH_BASE64_LENGTH];

lib/src/headers/email_address.nr

+22-13
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,32 @@
1-
use dep::std::collections::bounded_vec::BoundedVec;
21
use crate::{
32
Sequence, MAX_EMAIL_ADDRESS_LENGTH, EMAIL_ADDRESS_CHAR_TABLE,
4-
headers::constrain_header_field_detect_last_angle_bracket
3+
headers::constrain_header_field_detect_last_angle_bracket,
54
};
65

76
pub fn get_email_address<let MAX_HEADER_LENGTH: u32, let HEADER_FIELD_NAME_LENGTH: u32>(
87
header: BoundedVec<u8, MAX_HEADER_LENGTH>,
98
header_field_sequence: Sequence,
109
email_address_sequence: Sequence,
11-
header_field_name: [u8; HEADER_FIELD_NAME_LENGTH]
10+
header_field_name: [u8; HEADER_FIELD_NAME_LENGTH],
1211
) -> BoundedVec<u8, MAX_EMAIL_ADDRESS_LENGTH> {
1312
// check field is uninterrupted and matches the expected field name
14-
let last_angle_bracket = constrain_header_field_detect_last_angle_bracket::<MAX_HEADER_LENGTH, MAX_EMAIL_ADDRESS_LENGTH + HEADER_FIELD_NAME_LENGTH + 1, HEADER_FIELD_NAME_LENGTH>(header, header_field_sequence, header_field_name);
13+
let last_angle_bracket = constrain_header_field_detect_last_angle_bracket::<MAX_HEADER_LENGTH, MAX_EMAIL_ADDRESS_LENGTH + HEADER_FIELD_NAME_LENGTH + 1, HEADER_FIELD_NAME_LENGTH>(
14+
header,
15+
header_field_sequence,
16+
header_field_name,
17+
);
1518
// if angle bracket found, assert index is +1
1619
if last_angle_bracket != 0 {
1720
assert(
18-
email_address_sequence.index == last_angle_bracket + 1, "Email address must start immediately after '<' if bracket is present"
21+
email_address_sequence.index == last_angle_bracket + 1,
22+
"Email address must start immediately after '<' if bracket is present",
1923
);
2024
}
2125
// check email sequence is within header field
2226
assert(
2327
email_address_sequence.index >= header_field_sequence.index
24-
& email_address_sequence.end_index() <= header_field_sequence.end_index(), "Email address sequence out of bounds"
28+
& email_address_sequence.end_index() <= header_field_sequence.end_index(),
29+
"Email address sequence out of bounds",
2530
);
2631

2732
// constrained get email address
@@ -30,18 +35,22 @@ pub fn get_email_address<let MAX_HEADER_LENGTH: u32, let HEADER_FIELD_NAME_LENGT
3035

3136
pub fn parse_email_address<let MAX_HEADER_LENGTH: u32>(
3237
header: BoundedVec<u8, MAX_HEADER_LENGTH>,
33-
email_address_sequence: Sequence
38+
email_address_sequence: Sequence,
3439
) -> BoundedVec<u8, MAX_EMAIL_ADDRESS_LENGTH> {
3540
// check the sequence is proceeded by an acceptable character
3641
if email_address_sequence.index != 0 {
3742
assert(
38-
EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked(email_address_sequence.index - 1)] == 2, "Email address must start with an acceptable character"
43+
EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked(email_address_sequence.index - 1)] == 2,
44+
"Email address must start with an acceptable character",
3945
);
4046
}
4147
if email_address_sequence.end_index() < header.len() {
4248
assert(
43-
EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked(email_address_sequence.index + email_address_sequence.length)]
44-
== 3, "Email address must end with an acceptable character"
49+
EMAIL_ADDRESS_CHAR_TABLE[header.get_unchecked(
50+
email_address_sequence.index + email_address_sequence.length,
51+
)]
52+
== 3,
53+
"Email address must end with an acceptable character",
4554
);
4655
}
4756
// check the email address and assign
@@ -50,15 +59,15 @@ pub fn parse_email_address<let MAX_HEADER_LENGTH: u32>(
5059
let index = email_address_sequence.index + i;
5160
if index < email_address_sequence.end_index() {
5261
let letter = header.get_unchecked(index);
53-
email_address.storage[i] = letter;
62+
email_address.set_unchecked(i, letter);
5463
assert(
55-
EMAIL_ADDRESS_CHAR_TABLE[letter] == 1, "Email address must only contain acceptable characters"
64+
EMAIL_ADDRESS_CHAR_TABLE[letter] == 1,
65+
"Email address must only contain acceptable characters",
5666
);
5767
}
5868
}
5969
email_address.len = email_address_sequence.length;
6070
// todo: should probably introduce a check for @
61-
6271
email_address
6372
}
6473

0 commit comments

Comments
 (0)