Skip to content

Commit 3665e6c

Browse files
authored
Merge pull request #25 from reclaimprotocol/multi-nonce
Multi Nonce Implementation for Gnark ZK Proofs
2 parents af4bb82 + 17cc12b commit 3665e6c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+2320
-1669
lines changed

.github/workflows/test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
runs-on: ubuntu-latest
1313
strategy:
1414
matrix:
15-
node-version: [22]
15+
node-version: [24]
1616
package-dir: [js]
1717

1818
steps:

bin/gnark/darwin-arm64-libprove.so

274 KB
Binary file not shown.
-97 KB
Binary file not shown.

bin/gnark/linux-arm64-libprove.so

243 KB
Binary file not shown.

bin/gnark/linux-arm64-libverify.so

-26.8 KB
Binary file not shown.

bin/gnark/linux-x86_64-libprove.so

507 KB
Binary file not shown.
33.3 KB
Binary file not shown.

gnark/circuits/aesV2/aes128_test.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,28 @@ func TestAES128(t *testing.T) {
2323
Nonce := "006CB6DBC0543B59DA48D90B"
2424
Counter := 1
2525

26-
// calculate ciphertext ourselves
26+
// calculate ciphertext ourselves for each block
2727
block, err := aes.NewCipher(mustHex(key))
2828
if err != nil {
2929
panic(err)
3030
}
31-
ctr := cipher.NewCTR(block, append(mustHex(Nonce), binary.BigEndian.AppendUint32(nil, uint32(Counter))...))
32-
ciphertext := make([]byte, len(mustHex(plaintext)))
33-
ctr.XORKeyStream(ciphertext, mustHex(plaintext))
31+
plaintextBytes := mustHex(plaintext)
32+
ciphertext := make([]byte, len(plaintextBytes))
33+
34+
// Process each block with its own counter
35+
blockSize := 16
36+
for b := 0; b < BLOCKS; b++ {
37+
start := b * blockSize
38+
end := start + blockSize
39+
if end > len(plaintextBytes) {
40+
end = len(plaintextBytes)
41+
}
42+
43+
// Create CTR mode with the counter for this block
44+
iv := append(mustHex(Nonce), binary.BigEndian.AppendUint32(nil, uint32(Counter+b))...)
45+
ctr := cipher.NewCTR(block, iv)
46+
ctr.XORKeyStream(ciphertext[start:end], plaintextBytes[start:end])
47+
}
3448

3549
keyAssign := StrToIntSlice(key, true)
3650
ptAssign := StrToIntSlice(plaintext, true)
@@ -41,8 +55,8 @@ func TestAES128(t *testing.T) {
4155
assignment := AESCircuit{
4256
AESBaseCircuit: AESBaseCircuit{
4357
Key: make([]frontend.Variable, 16),
44-
Counter: Counter,
45-
Nonce: [12]frontend.Variable{},
58+
Counter: [BLOCKS]frontend.Variable{},
59+
Nonce: [BLOCKS][12]frontend.Variable{},
4660
In: [BLOCKS * 16]frontend.Variable{},
4761
},
4862
Out: [BLOCKS * 16]frontend.Variable{},
@@ -59,15 +73,20 @@ func TestAES128(t *testing.T) {
5973
assignment.Out[i] = ctAssign[i]
6074
}
6175

62-
for i := 0; i < len(nonceAssign); i++ {
63-
assignment.Nonce[i] = nonceAssign[i]
76+
// Set the same nonce for all blocks in this test
77+
for b := 0; b < BLOCKS; b++ {
78+
for i := 0; i < len(nonceAssign); i++ {
79+
assignment.Nonce[b][i] = nonceAssign[i]
80+
}
81+
// Set counter for each block (incrementing)
82+
assignment.Counter[b] = Counter + b
6483
}
6584

6685
assert.CheckCircuit(&AESCircuit{
6786
AESBaseCircuit: AESBaseCircuit{
6887
Key: make([]frontend.Variable, 16),
69-
Counter: Counter,
70-
Nonce: [12]frontend.Variable{},
88+
Counter: [BLOCKS]frontend.Variable{},
89+
Nonce: [BLOCKS][12]frontend.Variable{},
7190
In: [BLOCKS * 16]frontend.Variable{},
7291
},
7392
Out: [BLOCKS * 16]frontend.Variable{},

gnark/circuits/aesV2/aes256_test.go

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,28 @@ func TestAES256(t *testing.T) {
2323
Nonce := "00FAAC24C1585EF15A43D875"
2424
Counter := 1
2525

26-
// calculate ciphertext ourselves
26+
// calculate ciphertext ourselves for each block
2727
block, err := aes.NewCipher(mustHex(key))
2828
if err != nil {
2929
panic(err)
3030
}
31-
ctr := cipher.NewCTR(block, append(mustHex(Nonce), binary.BigEndian.AppendUint32(nil, uint32(Counter))...))
32-
ciphertext := make([]byte, len(mustHex(plaintext)))
33-
ctr.XORKeyStream(ciphertext, mustHex(plaintext))
31+
plaintextBytes := mustHex(plaintext)
32+
ciphertext := make([]byte, len(plaintextBytes))
33+
34+
// Process each block with its own counter
35+
blockSize := 16
36+
for b := 0; b < BLOCKS; b++ {
37+
start := b * blockSize
38+
end := start + blockSize
39+
if end > len(plaintextBytes) {
40+
end = len(plaintextBytes)
41+
}
42+
43+
// Create CTR mode with the counter for this block
44+
iv := append(mustHex(Nonce), binary.BigEndian.AppendUint32(nil, uint32(Counter+b))...)
45+
ctr := cipher.NewCTR(block, iv)
46+
ctr.XORKeyStream(ciphertext[start:end], plaintextBytes[start:end])
47+
}
3448

3549
keyAssign := StrToIntSlice(key, true)
3650
ptAssign := StrToIntSlice(plaintext, true)
@@ -41,8 +55,8 @@ func TestAES256(t *testing.T) {
4155
assignment := AESCircuit{
4256
AESBaseCircuit: AESBaseCircuit{
4357
Key: make([]frontend.Variable, 32),
44-
Counter: Counter,
45-
Nonce: [12]frontend.Variable{},
58+
Counter: [BLOCKS]frontend.Variable{},
59+
Nonce: [BLOCKS][12]frontend.Variable{},
4660
In: [BLOCKS * 16]frontend.Variable{},
4761
},
4862
Out: [BLOCKS * 16]frontend.Variable{},
@@ -59,15 +73,20 @@ func TestAES256(t *testing.T) {
5973
assignment.Out[i] = ciphertext[i]
6074
}
6175

62-
for i := 0; i < len(nonceAssign); i++ {
63-
assignment.Nonce[i] = nonceAssign[i]
76+
// Set the same nonce for all blocks in this test
77+
for b := 0; b < BLOCKS; b++ {
78+
for i := 0; i < len(nonceAssign); i++ {
79+
assignment.Nonce[b][i] = nonceAssign[i]
80+
}
81+
// Set counter for each block (incrementing)
82+
assignment.Counter[b] = Counter + b
6483
}
6584

6685
assert.CheckCircuit(&AESCircuit{
6786
AESBaseCircuit: AESBaseCircuit{
6887
Key: make([]frontend.Variable, 32),
69-
Counter: Counter,
70-
Nonce: [12]frontend.Variable{},
88+
Counter: [BLOCKS]frontend.Variable{},
89+
Nonce: [BLOCKS][12]frontend.Variable{},
7190
In: [BLOCKS * 16]frontend.Variable{},
7291
},
7392
Out: [BLOCKS * 16]frontend.Variable{},

gnark/circuits/aesV2/common.go

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,16 @@ const NB = 4
1212

1313
type AESBaseCircuit struct {
1414
Key []frontend.Variable
15-
Nonce [12]frontend.Variable `gnark:",public"`
16-
Counter frontend.Variable `gnark:",public"`
15+
Nonce [BLOCKS][12]frontend.Variable `gnark:",public"`
16+
Counter [BLOCKS]frontend.Variable `gnark:",public"`
1717
In [BLOCKS * 16]frontend.Variable `gnark:",public"`
1818
}
1919

2020
type AESGadget struct {
2121
api frontend.API
22-
sbox *logderivlookup.Table
22+
sbox logderivlookup.Table
2323
RCon [11]frontend.Variable
24-
t0, t1, t2, t3 *logderivlookup.Table
24+
t0, t1, t2, t3 logderivlookup.Table
2525
keySize int
2626
}
2727

@@ -53,25 +53,24 @@ func (aes *AESBaseCircuit) Define(api frontend.API, out [BLOCKS * 16]frontend.Va
5353
return errors.New("key size must be 16 or 32")
5454
}
5555

56-
counter := aes.Counter
5756
var counterBlock [16]frontend.Variable
5857

5958
gAes := NewAESGadget(api, keySize)
6059

61-
for i := 0; i < 12; i++ {
62-
counterBlock[i] = aes.Nonce[i]
63-
}
6460
for b := 0; b < BLOCKS; b++ {
65-
gAes.createIV(counter, counterBlock[:])
61+
// Use per-block nonce
62+
for i := 0; i < 12; i++ {
63+
counterBlock[i] = aes.Nonce[b][i]
64+
}
65+
// Use per-block counter
66+
gAes.createIV(aes.Counter[b], counterBlock[:])
6667
// encrypt counter under key
6768
keystream := gAes.Encrypt(aes.Key, counterBlock)
6869

6970
for i := 0; i < 16; i++ {
7071
api.AssertIsEqual(out[b*16+i], gAes.VariableXor(keystream[i], aes.In[b*16+i], 8))
7172
}
72-
counter = api.Add(counter, 1)
7373
}
74-
api.AssertIsEqual(counter, api.Add(aes.Counter, BLOCKS))
7574

7675
return nil
7776
}
@@ -135,7 +134,7 @@ func (aes *AESGadget) ShiftSub(state [16]frontend.Variable) []frontend.Variable
135134
}
136135

137136
// substitute word with naive lookup of sbox
138-
func (aes *AESGadget) Subws(sbox *logderivlookup.Table, a ...frontend.Variable) []frontend.Variable {
137+
func (aes *AESGadget) Subws(sbox logderivlookup.Table, a ...frontend.Variable) []frontend.Variable {
139138
return sbox.Lookup(a...)
140139
}
141140

0 commit comments

Comments
 (0)