diff --git a/bin/gnark/linux-arm64-libprove.so b/bin/gnark/linux-arm64-libprove.so index 61c0122..bccdd11 100644 Binary files a/bin/gnark/linux-arm64-libprove.so and b/bin/gnark/linux-arm64-libprove.so differ diff --git a/bin/gnark/linux-arm64-libverify.so b/bin/gnark/linux-arm64-libverify.so index 43054d2..427962a 100644 Binary files a/bin/gnark/linux-arm64-libverify.so and b/bin/gnark/linux-arm64-libverify.so differ diff --git a/bin/gnark/linux-x86_64-libprove.so b/bin/gnark/linux-x86_64-libprove.so index 4d7dafb..d0cdf40 100644 Binary files a/bin/gnark/linux-x86_64-libprove.so and b/bin/gnark/linux-x86_64-libprove.so differ diff --git a/bin/gnark/linux-x86_64-libverify.so b/bin/gnark/linux-x86_64-libverify.so index 96253b4..13fdd46 100644 Binary files a/bin/gnark/linux-x86_64-libverify.so and b/bin/gnark/linux-x86_64-libverify.so differ diff --git a/gnark/circuits/aesV2/aes.go b/gnark/circuits/aesV2/aes.go index 8918779..7a182d4 100644 --- a/gnark/circuits/aesV2/aes.go +++ b/gnark/circuits/aesV2/aes.go @@ -1,249 +1,12 @@ package aes_v2 -import ( - "errors" +import "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/lookup/logderivlookup" -) - -const BLOCKS = 5 -const NB = 4 - -type AESWrapper struct { - Key []frontend.Variable - Nonce [12]frontend.Variable `gnark:",public"` - Counter frontend.Variable `gnark:",public"` - In [BLOCKS * 16]frontend.Variable `gnark:",public"` - Out [BLOCKS * 16]frontend.Variable `gnark:",public"` -} - -type AESGadget struct { - api frontend.API - sbox *logderivlookup.Table - RCon [11]frontend.Variable - t0, t1, t2, t3 *logderivlookup.Table - keySize int -} - -// retuns AESGadget instance which can be used inside a circuit -func NewAESGadget(api frontend.API, keySize int) AESGadget { - - t0 := logderivlookup.New(api) - t1 := logderivlookup.New(api) - t2 := logderivlookup.New(api) - t3 := logderivlookup.New(api) - sbox := logderivlookup.New(api) - for i := 0; i < 256; i++ { - t0.Insert(T[0][i]) - t1.Insert(T[1][i]) - t2.Insert(T[2][i]) - t3.Insert(T[3][i]) - sbox.Insert(sbox0[i]) - } - - RCon := [11]frontend.Variable{0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36} - - return AESGadget{api: api, sbox: sbox, RCon: RCon, t0: t0, t1: t1, t2: t2, t3: t3, keySize: keySize} -} - -func (aes *AESWrapper) Define(api frontend.API) error { - keySize := len(aes.Key) - - if keySize != 16 && keySize != 32 { - return errors.New("key size must be 16 or 32") - } - - counter := aes.Counter - var counterBlock [16]frontend.Variable - - gAes := NewAESGadget(api, keySize) - - for i := 0; i < 12; i++ { - counterBlock[i] = aes.Nonce[i] - } - for b := 0; b < BLOCKS; b++ { - gAes.createIV(counter, counterBlock[:]) - // encrypt counter under key - keystream := gAes.Encrypt(aes.Key, counterBlock) - - for i := 0; i < 16; i++ { - api.AssertIsEqual(aes.Out[b*16+i], gAes.VariableXor(keystream[i], aes.In[b*16+i], 8)) - } - counter = api.Add(counter, 1) - } - api.AssertIsEqual(counter, api.Add(aes.Counter, BLOCKS)) - - return nil -} - -// aes128 encrypt function -func (aes *AESGadget) SubBytes(state [16]frontend.Variable) (res [16]frontend.Variable) { - t := aes.Subws(aes.sbox, state[:]...) - copy(res[:], t) - return res -} - -// xor on bits of two frontend.Variables -func (aes *AESGadget) VariableXor(a frontend.Variable, b frontend.Variable, size int) frontend.Variable { - bitsA := aes.api.ToBinary(a, size) - bitsB := aes.api.ToBinary(b, size) - x := make([]frontend.Variable, size) - for i := 0; i < size; i++ { - x[i] = aes.api.Xor(bitsA[i], bitsB[i]) - } - return aes.api.FromBinary(x...) -} - -func (aes *AESGadget) XorSubWords(a, b, c, d frontend.Variable, xk []frontend.Variable) []frontend.Variable { - - aa := aes.t0.Lookup(a)[0] - bb := aes.t1.Lookup(b)[0] - cc := aes.t2.Lookup(c)[0] - dd := aes.t3.Lookup(d)[0] - - t0 := aes.api.ToBinary(aa, 32) - t1 := aes.api.ToBinary(bb, 32) - t2 := aes.api.ToBinary(cc, 32) - t3 := aes.api.ToBinary(dd, 32) - - t4 := append(aes.api.ToBinary(xk[0], 8), aes.api.ToBinary(xk[1], 8)...) - t4 = append(t4, aes.api.ToBinary(xk[2], 8)...) - t4 = append(t4, aes.api.ToBinary(xk[3], 8)...) - - t := make([]frontend.Variable, 32) - for i := 0; i < 32; i++ { - t[i] = aes.api.Xor(t0[i], t1[i]) - t[i] = aes.api.Xor(t[i], t2[i]) - t[i] = aes.api.Xor(t[i], t3[i]) - t[i] = aes.api.Xor(t[i], t4[i]) - } - - newWord := make([]frontend.Variable, 4) - newWord[0] = aes.api.FromBinary(t[:8]...) - newWord[1] = aes.api.FromBinary(t[8:16]...) - newWord[2] = aes.api.FromBinary(t[16:24]...) - newWord[3] = aes.api.FromBinary(t[24:32]...) - return newWord -} - -func (aes *AESGadget) ShiftSub(state [16]frontend.Variable) []frontend.Variable { - t := make([]frontend.Variable, 16) - for i := 0; i < 16; i++ { - t[i] = state[byte_order[i]] - } - return aes.Subws(aes.sbox, t...) -} - -// substitute word with naive lookup of sbox -func (aes *AESGadget) Subws(sbox *logderivlookup.Table, a ...frontend.Variable) []frontend.Variable { - return sbox.Lookup(a...) -} - -func (aes *AESGadget) createIV(counter frontend.Variable, iv []frontend.Variable) { - aBits := aes.api.ToBinary(counter, 32) - - for i := 0; i < 4; i++ { - iv[15-i] = aes.api.FromBinary(aBits[i*8 : i*8+8]...) - } -} - -func (aes *AESGadget) Encrypt(key []frontend.Variable, pt [16]frontend.Variable) [16]frontend.Variable { - keySize := aes.keySize - rounds := 10 - if keySize == 32 { - rounds = 14 - } - - // expand key - xk := aes.ExpandKey(key) - var state [16]frontend.Variable - for i := 0; i < 16; i++ { - state[i] = aes.VariableXor(xk[i], pt[i], 8) - } - - var t0, t1, t2, t3 []frontend.Variable - // iterate rounds - for i := 1; i < rounds; i++ { - k := i * 16 - t0 = aes.XorSubWords(state[0], state[5], state[10], state[15], xk[k+0:k+4]) - t1 = aes.XorSubWords(state[4], state[9], state[14], state[3], xk[k+4:k+8]) - t2 = aes.XorSubWords(state[8], state[13], state[2], state[7], xk[k+8:k+12]) - t3 = aes.XorSubWords(state[12], state[1], state[6], state[11], xk[k+12:k+16]) - - copy(state[:4], t0) - copy(state[4:8], t1) - copy(state[8:12], t2) - copy(state[12:16], t3) - } - - copy(state[:], aes.ShiftSub(state)) - - k := rounds * 16 - - for i := 0; i < 4; i++ { - state[i+0] = aes.VariableXor(state[i+0], xk[k+i+0], 8) - state[i+4] = aes.VariableXor(state[i+4], xk[k+i+4], 8) - state[i+8] = aes.VariableXor(state[i+8], xk[k+i+8], 8) - state[i+12] = aes.VariableXor(state[i+12], xk[k+i+12], 8) - } - - return state +type AESCircuit struct { + AESBaseCircuit + Out [BLOCKS * 16]frontend.Variable `gnark:",public"` } -func (aes *AESGadget) ExpandKey(key []frontend.Variable) []frontend.Variable { - - keySize := aes.keySize - rounds := 10 - if keySize == 32 { - rounds = 14 - } - - var nWords = NB * (rounds + 1) - - expand := make([]frontend.Variable, nWords*4) - i := 0 - - for i < keySize { - expand[i] = key[i] - expand[i+1] = key[i+1] - expand[i+2] = key[i+2] - expand[i+3] = key[i+3] - - i += 4 - } - - for i < (nWords * 4) { - t0 := expand[i-4] - t1 := expand[i-3] - t2 := expand[i-2] - t3 := expand[i-1] - - if i%keySize == 0 { - // rotation - t0, t1, t2, t3 = t1, t2, t3, t0 - - // sub words - tt := aes.Subws(aes.sbox, t0, t1, t2, t3) - t0, t1, t2, t3 = tt[0], tt[1], tt[2], tt[3] - - t0 = aes.VariableXor(t0, aes.RCon[i/keySize], 8) - } - - if rounds == 14 && i%keySize == 16 { - // sub words - tt := aes.Subws(aes.sbox, t0, t1, t2, t3) - t0, t1, t2, t3 = tt[0], tt[1], tt[2], tt[3] - - } - - expand[i] = aes.VariableXor(expand[i-keySize], t0, 8) - expand[i+1] = aes.VariableXor(expand[i-keySize+1], t1, 8) - expand[i+2] = aes.VariableXor(expand[i-keySize+2], t2, 8) - expand[i+3] = aes.VariableXor(expand[i-keySize+3], t3, 8) - - i += 4 - } - - return expand +func (c *AESCircuit) Define(api frontend.API) error { + return c.AESBaseCircuit.Define(api, c.Out) } diff --git a/gnark/circuits/aesV2/aes128_test.go b/gnark/circuits/aesV2/aes128_test.go index f2a5913..affe5ac 100644 --- a/gnark/circuits/aesV2/aes128_test.go +++ b/gnark/circuits/aesV2/aes128_test.go @@ -38,12 +38,14 @@ func TestAES128(t *testing.T) { nonceAssign := StrToIntSlice(Nonce, true) // witness values preparation - assignment := AESWrapper{ - Key: make([]frontend.Variable, 16), - Counter: Counter, - Nonce: [12]frontend.Variable{}, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, + assignment := AESCircuit{ + AESBaseCircuit: AESBaseCircuit{ + Key: make([]frontend.Variable, 16), + Counter: Counter, + Nonce: [12]frontend.Variable{}, + In: [BLOCKS * 16]frontend.Variable{}, + }, + Out: [BLOCKS * 16]frontend.Variable{}, } // assign values here because required to use make in assignment @@ -61,12 +63,14 @@ func TestAES128(t *testing.T) { assignment.Nonce[i] = nonceAssign[i] } - assert.CheckCircuit(&AESWrapper{ - Key: make([]frontend.Variable, 16), - Counter: Counter, - Nonce: [12]frontend.Variable{}, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, + assert.CheckCircuit(&AESCircuit{ + AESBaseCircuit: AESBaseCircuit{ + Key: make([]frontend.Variable, 16), + Counter: Counter, + Nonce: [12]frontend.Variable{}, + In: [BLOCKS * 16]frontend.Variable{}, + }, + Out: [BLOCKS * 16]frontend.Variable{}, }, test.WithValidAssignment(&assignment)) } @@ -97,8 +101,10 @@ func mustHex(s string) []byte { func TestCompile(t *testing.T) { curve := ecc.BN254.ScalarField() - witness := AESWrapper{ - Key: make([]frontend.Variable, 16), + witness := AESCircuit{ + AESBaseCircuit: AESBaseCircuit{ + Key: make([]frontend.Variable, 16), + }, } r1css, err := frontend.Compile(curve, r1cs.NewBuilder, &witness) diff --git a/gnark/circuits/aesV2/aes256_test.go b/gnark/circuits/aesV2/aes256_test.go index 5a85c4d..e2dd5ac 100644 --- a/gnark/circuits/aesV2/aes256_test.go +++ b/gnark/circuits/aesV2/aes256_test.go @@ -38,12 +38,14 @@ func TestAES256(t *testing.T) { nonceAssign := StrToIntSlice(Nonce, true) // witness values preparation - assignment := AESWrapper{ - Key: make([]frontend.Variable, 32), - Counter: Counter, - Nonce: [12]frontend.Variable{}, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, + assignment := AESCircuit{ + AESBaseCircuit: AESBaseCircuit{ + Key: make([]frontend.Variable, 32), + Counter: Counter, + Nonce: [12]frontend.Variable{}, + In: [BLOCKS * 16]frontend.Variable{}, + }, + Out: [BLOCKS * 16]frontend.Variable{}, } // assign values here because required to use make in assignment @@ -61,20 +63,24 @@ func TestAES256(t *testing.T) { assignment.Nonce[i] = nonceAssign[i] } - assert.CheckCircuit(&AESWrapper{ - Key: make([]frontend.Variable, 32), - Counter: Counter, - Nonce: [12]frontend.Variable{}, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, + assert.CheckCircuit(&AESCircuit{ + AESBaseCircuit: AESBaseCircuit{ + Key: make([]frontend.Variable, 32), + Counter: Counter, + Nonce: [12]frontend.Variable{}, + In: [BLOCKS * 16]frontend.Variable{}, + }, + Out: [BLOCKS * 16]frontend.Variable{}, }, test.WithValidAssignment(&assignment)) } func TestCompile256(t *testing.T) { curve := ecc.BN254.ScalarField() - witness := AESWrapper{ - Key: make([]frontend.Variable, 32), + witness := AESCircuit{ + AESBaseCircuit: AESBaseCircuit{ + Key: make([]frontend.Variable, 32), + }, } r1css, err := frontend.Compile(curve, r1cs.NewBuilder, &witness) diff --git a/gnark/circuits/aesV2/common.go b/gnark/circuits/aesV2/common.go new file mode 100644 index 0000000..a868bce --- /dev/null +++ b/gnark/circuits/aesV2/common.go @@ -0,0 +1,248 @@ +package aes_v2 + +import ( + "errors" + + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/lookup/logderivlookup" +) + +const BLOCKS = 5 +const NB = 4 + +type AESBaseCircuit struct { + Key []frontend.Variable + Nonce [12]frontend.Variable `gnark:",public"` + Counter frontend.Variable `gnark:",public"` + In [BLOCKS * 16]frontend.Variable `gnark:",public"` +} + +type AESGadget struct { + api frontend.API + sbox *logderivlookup.Table + RCon [11]frontend.Variable + t0, t1, t2, t3 *logderivlookup.Table + keySize int +} + +// retuns AESGadget instance which can be used inside a circuit +func NewAESGadget(api frontend.API, keySize int) AESGadget { + + t0 := logderivlookup.New(api) + t1 := logderivlookup.New(api) + t2 := logderivlookup.New(api) + t3 := logderivlookup.New(api) + sbox := logderivlookup.New(api) + for i := 0; i < 256; i++ { + t0.Insert(T[0][i]) + t1.Insert(T[1][i]) + t2.Insert(T[2][i]) + t3.Insert(T[3][i]) + sbox.Insert(sbox0[i]) + } + + RCon := [11]frontend.Variable{0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36} + + return AESGadget{api: api, sbox: sbox, RCon: RCon, t0: t0, t1: t1, t2: t2, t3: t3, keySize: keySize} +} + +func (aes *AESBaseCircuit) Define(api frontend.API, out [BLOCKS * 16]frontend.Variable) error { + keySize := len(aes.Key) + + if keySize != 16 && keySize != 32 { + return errors.New("key size must be 16 or 32") + } + + counter := aes.Counter + var counterBlock [16]frontend.Variable + + gAes := NewAESGadget(api, keySize) + + for i := 0; i < 12; i++ { + counterBlock[i] = aes.Nonce[i] + } + for b := 0; b < BLOCKS; b++ { + gAes.createIV(counter, counterBlock[:]) + // encrypt counter under key + keystream := gAes.Encrypt(aes.Key, counterBlock) + + for i := 0; i < 16; i++ { + api.AssertIsEqual(out[b*16+i], gAes.VariableXor(keystream[i], aes.In[b*16+i], 8)) + } + counter = api.Add(counter, 1) + } + api.AssertIsEqual(counter, api.Add(aes.Counter, BLOCKS)) + + return nil +} + +// aes128 encrypt function +func (aes *AESGadget) SubBytes(state [16]frontend.Variable) (res [16]frontend.Variable) { + t := aes.Subws(aes.sbox, state[:]...) + copy(res[:], t) + return res +} + +// xor on bits of two frontend.Variables +func (aes *AESGadget) VariableXor(a frontend.Variable, b frontend.Variable, size int) frontend.Variable { + bitsA := aes.api.ToBinary(a, size) + bitsB := aes.api.ToBinary(b, size) + x := make([]frontend.Variable, size) + for i := 0; i < size; i++ { + x[i] = aes.api.Xor(bitsA[i], bitsB[i]) + } + return aes.api.FromBinary(x...) +} + +func (aes *AESGadget) XorSubWords(a, b, c, d frontend.Variable, xk []frontend.Variable) []frontend.Variable { + + aa := aes.t0.Lookup(a)[0] + bb := aes.t1.Lookup(b)[0] + cc := aes.t2.Lookup(c)[0] + dd := aes.t3.Lookup(d)[0] + + t0 := aes.api.ToBinary(aa, 32) + t1 := aes.api.ToBinary(bb, 32) + t2 := aes.api.ToBinary(cc, 32) + t3 := aes.api.ToBinary(dd, 32) + + t4 := append(aes.api.ToBinary(xk[0], 8), aes.api.ToBinary(xk[1], 8)...) + t4 = append(t4, aes.api.ToBinary(xk[2], 8)...) + t4 = append(t4, aes.api.ToBinary(xk[3], 8)...) + + t := make([]frontend.Variable, 32) + for i := 0; i < 32; i++ { + t[i] = aes.api.Xor(t0[i], t1[i]) + t[i] = aes.api.Xor(t[i], t2[i]) + t[i] = aes.api.Xor(t[i], t3[i]) + t[i] = aes.api.Xor(t[i], t4[i]) + } + + newWord := make([]frontend.Variable, 4) + newWord[0] = aes.api.FromBinary(t[:8]...) + newWord[1] = aes.api.FromBinary(t[8:16]...) + newWord[2] = aes.api.FromBinary(t[16:24]...) + newWord[3] = aes.api.FromBinary(t[24:32]...) + return newWord +} + +func (aes *AESGadget) ShiftSub(state [16]frontend.Variable) []frontend.Variable { + t := make([]frontend.Variable, 16) + for i := 0; i < 16; i++ { + t[i] = state[byte_order[i]] + } + return aes.Subws(aes.sbox, t...) +} + +// substitute word with naive lookup of sbox +func (aes *AESGadget) Subws(sbox *logderivlookup.Table, a ...frontend.Variable) []frontend.Variable { + return sbox.Lookup(a...) +} + +func (aes *AESGadget) createIV(counter frontend.Variable, iv []frontend.Variable) { + aBits := aes.api.ToBinary(counter, 32) + + for i := 0; i < 4; i++ { + iv[15-i] = aes.api.FromBinary(aBits[i*8 : i*8+8]...) + } +} + +func (aes *AESGadget) Encrypt(key []frontend.Variable, pt [16]frontend.Variable) [16]frontend.Variable { + keySize := aes.keySize + rounds := 10 + if keySize == 32 { + rounds = 14 + } + + // expand key + xk := aes.ExpandKey(key) + var state [16]frontend.Variable + for i := 0; i < 16; i++ { + state[i] = aes.VariableXor(xk[i], pt[i], 8) + } + + var t0, t1, t2, t3 []frontend.Variable + // iterate rounds + for i := 1; i < rounds; i++ { + k := i * 16 + t0 = aes.XorSubWords(state[0], state[5], state[10], state[15], xk[k+0:k+4]) + t1 = aes.XorSubWords(state[4], state[9], state[14], state[3], xk[k+4:k+8]) + t2 = aes.XorSubWords(state[8], state[13], state[2], state[7], xk[k+8:k+12]) + t3 = aes.XorSubWords(state[12], state[1], state[6], state[11], xk[k+12:k+16]) + + copy(state[:4], t0) + copy(state[4:8], t1) + copy(state[8:12], t2) + copy(state[12:16], t3) + } + + copy(state[:], aes.ShiftSub(state)) + + k := rounds * 16 + + for i := 0; i < 4; i++ { + state[i+0] = aes.VariableXor(state[i+0], xk[k+i+0], 8) + state[i+4] = aes.VariableXor(state[i+4], xk[k+i+4], 8) + state[i+8] = aes.VariableXor(state[i+8], xk[k+i+8], 8) + state[i+12] = aes.VariableXor(state[i+12], xk[k+i+12], 8) + } + + return state +} + +func (aes *AESGadget) ExpandKey(key []frontend.Variable) []frontend.Variable { + + keySize := aes.keySize + rounds := 10 + if keySize == 32 { + rounds = 14 + } + + var nWords = NB * (rounds + 1) + + expand := make([]frontend.Variable, nWords*4) + i := 0 + + for i < keySize { + expand[i] = key[i] + expand[i+1] = key[i+1] + expand[i+2] = key[i+2] + expand[i+3] = key[i+3] + + i += 4 + } + + for i < (nWords * 4) { + t0 := expand[i-4] + t1 := expand[i-3] + t2 := expand[i-2] + t3 := expand[i-1] + + if i%keySize == 0 { + // rotation + t0, t1, t2, t3 = t1, t2, t3, t0 + + // sub words + tt := aes.Subws(aes.sbox, t0, t1, t2, t3) + t0, t1, t2, t3 = tt[0], tt[1], tt[2], tt[3] + + t0 = aes.VariableXor(t0, aes.RCon[i/keySize], 8) + } + + if rounds == 14 && i%keySize == 16 { + // sub words + tt := aes.Subws(aes.sbox, t0, t1, t2, t3) + t0, t1, t2, t3 = tt[0], tt[1], tt[2], tt[3] + + } + + expand[i] = aes.VariableXor(expand[i-keySize], t0, 8) + expand[i+1] = aes.VariableXor(expand[i-keySize+1], t1, 8) + expand[i+2] = aes.VariableXor(expand[i-keySize+2], t2, 8) + expand[i+3] = aes.VariableXor(expand[i-keySize+3], t3, 8) + + i += 4 + } + + return expand +} diff --git a/gnark/circuits/aesV2_oprf/aes.go b/gnark/circuits/aesV2_oprf/aes.go index c0c1195..e5bf659 100644 --- a/gnark/circuits/aesV2_oprf/aes.go +++ b/gnark/circuits/aesV2_oprf/aes.go @@ -1,326 +1,39 @@ package aes_v2_oprf import ( + aes_v2 "gnark-symmetric-crypto/circuits/aesV2" "gnark-symmetric-crypto/circuits/toprf" - "math/big" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/algebra/native/twistededwards" - "github.com/consensys/gnark/std/lookup/logderivlookup" - "github.com/consensys/gnark/std/math/cmp" ) -const BLOCKS = 5 -const NB = 4 -const BytesPerElement = 31 - -type TOPRFData struct { - DomainSeparator frontend.Variable `gnark:",public"` - Mask frontend.Variable - - EvaluatedElements [toprf.Threshold]twistededwards.Point `gnark:",public"` // responses per each node - Coefficients [toprf.Threshold]frontend.Variable `gnark:",public"` // coeffs for reconstructing element - - // Proofs of DLEQ per node - PublicKeys [toprf.Threshold]twistededwards.Point `gnark:",public"` - C [toprf.Threshold]frontend.Variable `gnark:",public"` - R [toprf.Threshold]frontend.Variable `gnark:",public"` - - Output frontend.Variable `gnark:",public"` -} - -type AESWrapper struct { - Key []frontend.Variable - Nonce [12]frontend.Variable `gnark:",public"` - Counter frontend.Variable `gnark:",public"` - In [BLOCKS * 16]frontend.Variable `gnark:",public"` - Out [BLOCKS * 16]frontend.Variable // plaintext - Bitmask [BLOCKS * 16 * 8]frontend.Variable `gnark:",public"` // bit mask for bytes being hashed - +type AESTOPRFCircuit struct { + aes_v2.AESBaseCircuit + Out [aes_v2.BLOCKS * 16]frontend.Variable + Bitmask [aes_v2.BLOCKS * 16 * 8]frontend.Variable `gnark:",public"` // bit mask for bytes being hashed // Length of "secret data" elements to be hashed. In bytes - Len frontend.Variable `gnark:",public"` - - TOPRF TOPRFData -} - -type AESGadget struct { - api frontend.API - sbox *logderivlookup.Table - RCon [11]frontend.Variable - t0, t1, t2, t3 *logderivlookup.Table - keySize int -} - -// retuns AESGadget instance which can be used inside a circuit -func NewAESGadget(api frontend.API, keySize int) AESGadget { - - t0 := logderivlookup.New(api) - t1 := logderivlookup.New(api) - t2 := logderivlookup.New(api) - t3 := logderivlookup.New(api) - sbox := logderivlookup.New(api) - for i := 0; i < 256; i++ { - t0.Insert(T[0][i]) - t1.Insert(T[1][i]) - t2.Insert(T[2][i]) - t3.Insert(T[3][i]) - sbox.Insert(sbox0[i]) - } - - RCon := [11]frontend.Variable{0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36} - - return AESGadget{api: api, sbox: sbox, RCon: RCon, t0: t0, t1: t1, t2: t2, t3: t3, keySize: keySize} -} - -func (aes *AESWrapper) Define(api frontend.API) error { - counter := aes.Counter - var counterBlock [16]frontend.Variable - - gAes := NewAESGadget(api, len(aes.Key)) - - for i := 0; i < 12; i++ { - counterBlock[i] = aes.Nonce[i] - } - for b := 0; b < BLOCKS; b++ { - gAes.createIV(counter, counterBlock[:]) - // encrypt counter under key - keystream := gAes.Encrypt(aes.Key, counterBlock) - - for i := 0; i < 16; i++ { - api.AssertIsEqual(aes.Out[b*16+i], gAes.VariableXor(keystream[i], aes.In[b*16+i], 8)) - } - counter = api.Add(counter, 1) - } - api.AssertIsEqual(counter, api.Add(aes.Counter, BLOCKS)) - - return aes.TOPRFVerify(api) -} - -// aes128 encrypt function -func (aes *AESGadget) SubBytes(state [16]frontend.Variable) (res [16]frontend.Variable) { - t := aes.Subws(aes.sbox, state[:]...) - copy(res[:], t) - return res -} - -// xor on bits of two frontend.Variables -func (aes *AESGadget) VariableXor(a frontend.Variable, b frontend.Variable, size int) frontend.Variable { - bitsA := aes.api.ToBinary(a, size) - bitsB := aes.api.ToBinary(b, size) - x := make([]frontend.Variable, size) - for i := 0; i < size; i++ { - x[i] = aes.api.Xor(bitsA[i], bitsB[i]) - } - return aes.api.FromBinary(x...) -} - -func (aes *AESGadget) XorSubWords(a, b, c, d frontend.Variable, xk []frontend.Variable) []frontend.Variable { - - aa := aes.t0.Lookup(a)[0] - bb := aes.t1.Lookup(b)[0] - cc := aes.t2.Lookup(c)[0] - dd := aes.t3.Lookup(d)[0] - - t0 := aes.api.ToBinary(aa, 32) - t1 := aes.api.ToBinary(bb, 32) - t2 := aes.api.ToBinary(cc, 32) - t3 := aes.api.ToBinary(dd, 32) - - t4 := append(aes.api.ToBinary(xk[0], 8), aes.api.ToBinary(xk[1], 8)...) - t4 = append(t4, aes.api.ToBinary(xk[2], 8)...) - t4 = append(t4, aes.api.ToBinary(xk[3], 8)...) - - t := make([]frontend.Variable, 32) - for i := 0; i < 32; i++ { - t[i] = aes.api.Xor(t0[i], t1[i]) - t[i] = aes.api.Xor(t[i], t2[i]) - t[i] = aes.api.Xor(t[i], t3[i]) - t[i] = aes.api.Xor(t[i], t4[i]) - } - - newWord := make([]frontend.Variable, 4) - newWord[0] = aes.api.FromBinary(t[:8]...) - newWord[1] = aes.api.FromBinary(t[8:16]...) - newWord[2] = aes.api.FromBinary(t[16:24]...) - newWord[3] = aes.api.FromBinary(t[24:32]...) - return newWord -} - -func (aes *AESGadget) ShiftSub(state [16]frontend.Variable) []frontend.Variable { - t := make([]frontend.Variable, 16) - for i := 0; i < 16; i++ { - t[i] = state[byte_order[i]] - } - return aes.Subws(aes.sbox, t...) -} - -// substitute word with naive lookup of sbox -func (aes *AESGadget) Subws(sbox *logderivlookup.Table, a ...frontend.Variable) []frontend.Variable { - return sbox.Lookup(a...) -} - -func (aes *AESGadget) createIV(counter frontend.Variable, iv []frontend.Variable) { - aBits := aes.api.ToBinary(counter, 32) - - for i := 0; i < 4; i++ { - iv[15-i] = aes.api.FromBinary(aBits[i*8 : i*8+8]...) - } -} - -func (aes *AESGadget) Encrypt(key []frontend.Variable, pt [16]frontend.Variable) [16]frontend.Variable { - keySize := aes.keySize - rounds := 10 - if keySize == 32 { - rounds = 14 - } - - // expand key - xk := aes.ExpandKey(key) - var state [16]frontend.Variable - for i := 0; i < 16; i++ { - state[i] = aes.VariableXor(xk[i], pt[i], 8) - } - - var t0, t1, t2, t3 []frontend.Variable - // iterate rounds - for i := 1; i < rounds; i++ { - k := i * 16 - t0 = aes.XorSubWords(state[0], state[5], state[10], state[15], xk[k+0:k+4]) - t1 = aes.XorSubWords(state[4], state[9], state[14], state[3], xk[k+4:k+8]) - t2 = aes.XorSubWords(state[8], state[13], state[2], state[7], xk[k+8:k+12]) - t3 = aes.XorSubWords(state[12], state[1], state[6], state[11], xk[k+12:k+16]) - - copy(state[:4], t0) - copy(state[4:8], t1) - copy(state[8:12], t2) - copy(state[12:16], t3) - } - - copy(state[:], aes.ShiftSub(state)) - - k := rounds * 16 - - for i := 0; i < 4; i++ { - state[i+0] = aes.VariableXor(state[i+0], xk[k+i+0], 8) - state[i+4] = aes.VariableXor(state[i+4], xk[k+i+4], 8) - state[i+8] = aes.VariableXor(state[i+8], xk[k+i+8], 8) - state[i+12] = aes.VariableXor(state[i+12], xk[k+i+12], 8) - } - - return state + Len frontend.Variable `gnark:",public"` + TOPRF toprf.Params } -func (aes *AESGadget) ExpandKey(key []frontend.Variable) []frontend.Variable { - - keySize := aes.keySize - rounds := 10 - if keySize == 32 { - rounds = 14 - } - - var nWords = NB * (rounds + 1) - - expand := make([]frontend.Variable, nWords*4) - i := 0 - - for i < keySize { - expand[i] = key[i] - expand[i+1] = key[i+1] - expand[i+2] = key[i+2] - expand[i+3] = key[i+3] - - i += 4 - } - - for i < (nWords * 4) { - t0 := expand[i-4] - t1 := expand[i-3] - t2 := expand[i-2] - t3 := expand[i-1] - - if i%keySize == 0 { - // rotation - t0, t1, t2, t3 = t1, t2, t3, t0 - - // sub words - tt := aes.Subws(aes.sbox, t0, t1, t2, t3) - t0, t1, t2, t3 = tt[0], tt[1], tt[2], tt[3] - - t0 = aes.VariableXor(t0, aes.RCon[i/keySize], 8) - } +func (c *AESTOPRFCircuit) Define(api frontend.API) error { - if rounds == 14 && i%keySize == 16 { - // subwords - tt := aes.Subws(aes.sbox, t0, t1, t2, t3) - t0, t1, t2, t3 = tt[0], tt[1], tt[2], tt[3] - - } - - expand[i] = aes.VariableXor(expand[i-keySize], t0, 8) - expand[i+1] = aes.VariableXor(expand[i-keySize+1], t1, 8) - expand[i+2] = aes.VariableXor(expand[i-keySize+2], t2, 8) - expand[i+3] = aes.VariableXor(expand[i-keySize+3], t3, 8) - - i += 4 + err := c.AESBaseCircuit.Define(api, c.Out) + if err != nil { + return err } - return expand -} - -func (circuit *AESWrapper) TOPRFVerify(api frontend.API) error { - outBits := make([]frontend.Variable, len(circuit.Out)*8) + outBits := make([]frontend.Variable, len(c.Out)*8) // flatten result bits array - for i := 0; i < len(circuit.Out); i++ { - bits := api.ToBinary(circuit.Out[i], 8) + for i := 0; i < len(c.Out); i++ { + bits := api.ToBinary(c.Out[i], 8) for j := 0; j < 8; j++ { outBits[i*8+j] = bits[j] } } - pow1 := frontend.Variable(1) - pow2 := frontend.Variable(0) - res1 := frontend.Variable(0) - res2 := frontend.Variable(0) - totalBits := frontend.Variable(0) - - for i := 0; i < len(outBits); i++ { - bitIndex := i - bitIsSet := circuit.Bitmask[bitIndex] - bit := api.Select(bitIsSet, outBits[bitIndex], 0) - - res1 = api.Add(res1, api.Mul(bit, pow1)) - res2 = api.Add(res2, api.Mul(bit, pow2)) - - n := api.Add(bitIsSet, 1) // do we need to multiply power by 2? - pow2 = api.Mul(pow2, n) - pow1 = api.Mul(pow1, n) - - totalBits = api.Add(totalBits, bitIsSet) - - r1Done := api.IsZero(api.Sub(totalBits, BytesPerElement*8)) // are we done with 1st number? - pow1 = api.Mul(pow1, api.Sub(1, r1Done)) // set pow1 to zero if yes - pow2 = api.Add(pow2, r1Done) // set pow2 to 1 to start increasing + c.TOPRF.SecretData = toprf.ExtractSecretElements(api, outBits, c.Bitmask[:], c.Len) - } - - api.AssertIsDifferent(circuit.Len, 0) // Len != 0 - - comparator := cmp.NewBoundedComparator(api, big.NewInt(512), false) // max diff is 512-496 - comparator.AssertIsLessEq(totalBits, BytesPerElement*8*2) // check that number of processed bits <= 62 bytes - api.AssertIsEqual(totalBits, api.Mul(circuit.Len, 8)) // and that it corresponds to Len - - // check that TOPRF output was created from secret data by a server with a specific public key - oprfData := &toprf.TOPRFParams{ - SecretData: [2]frontend.Variable{res1, res2}, - DomainSeparator: circuit.TOPRF.DomainSeparator, - Mask: circuit.TOPRF.Mask, - Responses: circuit.TOPRF.EvaluatedElements, - Coefficients: circuit.TOPRF.Coefficients, - Output: circuit.TOPRF.Output, - SharePublicKeys: circuit.TOPRF.PublicKeys, - C: circuit.TOPRF.C, - R: circuit.TOPRF.R, - } - return toprf.VerifyTOPRF(api, oprfData) + return toprf.VerifyTOPRF(api, &c.TOPRF) } diff --git a/gnark/circuits/aesV2_oprf/aes128_test.go b/gnark/circuits/aesV2_oprf/aes128_test.go index 37ea872..bba78e4 100644 --- a/gnark/circuits/aesV2_oprf/aes128_test.go +++ b/gnark/circuits/aesV2_oprf/aes128_test.go @@ -6,6 +6,7 @@ import ( "encoding/binary" "encoding/hex" "fmt" + aes_v2 "gnark-symmetric-crypto/circuits/aesV2" "gnark-symmetric-crypto/circuits/toprf" "gnark-symmetric-crypto/utils" "testing" @@ -19,7 +20,7 @@ import ( func TestAES128(t *testing.T) { assert := test.NewAssert(t) - key := "7E24067817FAE0D743D6CE1F32539163" + key := mustHex("7E24067817FAE0D743D6CE1F32539163") Nonce := "006CB6DBC0543B59DA48D90B" secretStr := "00000000001111111111000000000011000000000011111111110000000000" // max 62 bytes @@ -29,11 +30,11 @@ func TestAES128(t *testing.T) { pos := 18 Counter := 12345 - plaintext := make([]byte, BLOCKS*16) + plaintext := make([]byte, aes_v2.BLOCKS*16) copy(plaintext[pos:], secretBytes) // calculate ciphertext ourselves - block, err := aes.NewCipher(mustHex(key)) + block, err := aes.NewCipher(key) if err != nil { panic(err) } @@ -41,18 +42,15 @@ func TestAES128(t *testing.T) { ciphertext := make([]byte, len(plaintext)) ctr.XORKeyStream(ciphertext, plaintext) - keyAssign := mustHex(key) nonceAssign := mustHex(Nonce) - witness := createWitness(d, keyAssign, nonceAssign, Counter, ciphertext, plaintext, pos, len(secretBytes)) + witness := createWitness(d, key, nonceAssign, Counter, ciphertext, plaintext, pos, len(secretBytes)) - assert.CheckCircuit(&AESWrapper{ - Key: make([]frontend.Variable, 16), - Counter: Counter, - Nonce: [12]frontend.Variable{}, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, - }, test.WithValidAssignment(&witness), test.WithCurves(ecc.BN254)) + vKey := make([]frontend.Variable, len(key)) + for i := 0; i < len(vKey); i++ { + vKey[i] = key[i] + } + assert.CheckCircuit(&witness, test.WithValidAssignment(&witness), test.WithCurves(ecc.BN254)) r1css, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &witness) if err != nil { @@ -70,23 +68,26 @@ func mustHex(s string) []byte { return b } -func createWitness(d *toprf.TOPRFParams, bKey []uint8, bNonce []uint8, counter int, ciphertext []byte, plaintext []byte, pos, l int) AESWrapper { - witness := AESWrapper{ - Key: make([]frontend.Variable, 16), - Nonce: [12]frontend.Variable{}, - Counter: counter, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, - Len: l, - TOPRF: TOPRFData{ - Mask: d.Mask, - DomainSeparator: d.DomainSeparator, - EvaluatedElements: d.Responses, - Coefficients: d.Coefficients, - Output: d.Output, - PublicKeys: d.SharePublicKeys, - C: d.C, - R: d.R, +func createWitness(d *toprf.Params, bKey []uint8, bNonce []uint8, counter int, ciphertext []byte, plaintext []byte, pos, l int) AESTOPRFCircuit { + witness := AESTOPRFCircuit{ + AESBaseCircuit: aes_v2.AESBaseCircuit{ + Key: make([]frontend.Variable, 16), + Counter: counter, + Nonce: [12]frontend.Variable{}, + In: [aes_v2.BLOCKS * 16]frontend.Variable{}, + }, + Out: [aes_v2.BLOCKS * 16]frontend.Variable{}, + Len: l, + TOPRF: toprf.Params{ + SecretData: [2]frontend.Variable{0, 0}, // will be rewritten inside + Mask: d.Mask, + DomainSeparator: d.DomainSeparator, + Responses: d.Responses, + Coefficients: d.Coefficients, + Output: d.Output, + SharePublicKeys: d.SharePublicKeys, + C: d.C, + R: d.R, }, } diff --git a/gnark/circuits/aesV2_oprf/aes256_test.go b/gnark/circuits/aesV2_oprf/aes256_test.go index d04238c..d947d17 100644 --- a/gnark/circuits/aesV2_oprf/aes256_test.go +++ b/gnark/circuits/aesV2_oprf/aes256_test.go @@ -5,6 +5,7 @@ import ( "crypto/cipher" "encoding/binary" "fmt" + aes_v2 "gnark-symmetric-crypto/circuits/aesV2" "gnark-symmetric-crypto/circuits/toprf" "gnark-symmetric-crypto/utils" "testing" @@ -29,7 +30,7 @@ func TestAES256(t *testing.T) { pos := 30 Counter := 12345 - plaintext := make([]byte, BLOCKS*16) + plaintext := make([]byte, aes_v2.BLOCKS*16) copy(plaintext[pos:], secretBytes) // calculate ciphertext ourselves @@ -46,13 +47,7 @@ func TestAES256(t *testing.T) { witness := createWitness256(d, keyAssign, nonceAssign, Counter, ciphertext, plaintext, pos, len(secretBytes)) - assert.CheckCircuit(&AESWrapper{ - Key: make([]frontend.Variable, 32), - Counter: Counter, - Nonce: [12]frontend.Variable{}, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, - }, test.WithValidAssignment(&witness), test.WithCurves(ecc.BN254)) + assert.CheckCircuit(&witness, test.WithValidAssignment(&witness), test.WithCurves(ecc.BN254)) r1css, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, &witness) if err != nil { @@ -62,23 +57,26 @@ func TestAES256(t *testing.T) { fmt.Printf("constraints: %d\n", r1css.GetNbConstraints()) } -func createWitness256(d *toprf.TOPRFParams, bKey []uint8, bNonce []uint8, counter int, ciphertext []byte, plaintext []byte, pos, l int) AESWrapper { - witness := AESWrapper{ - Key: make([]frontend.Variable, 32), - Nonce: [12]frontend.Variable{}, - Counter: counter, - In: [BLOCKS * 16]frontend.Variable{}, - Out: [BLOCKS * 16]frontend.Variable{}, - Len: l, - TOPRF: TOPRFData{ - Mask: d.Mask, - DomainSeparator: d.DomainSeparator, - EvaluatedElements: d.Responses, - Coefficients: d.Coefficients, - Output: d.Output, - PublicKeys: d.SharePublicKeys, - C: d.C, - R: d.R, +func createWitness256(d *toprf.Params, bKey []uint8, bNonce []uint8, counter int, ciphertext []byte, plaintext []byte, pos, l int) AESTOPRFCircuit { + witness := AESTOPRFCircuit{ + AESBaseCircuit: aes_v2.AESBaseCircuit{ + Key: make([]frontend.Variable, 32), + Counter: counter, + Nonce: [12]frontend.Variable{}, + In: [aes_v2.BLOCKS * 16]frontend.Variable{}, + }, + Out: [aes_v2.BLOCKS * 16]frontend.Variable{}, + Len: l, + TOPRF: toprf.Params{ + SecretData: [2]frontend.Variable{0, 0}, // will be rewritten inside + Mask: d.Mask, + DomainSeparator: d.DomainSeparator, + Responses: d.Responses, + Coefficients: d.Coefficients, + Output: d.Output, + SharePublicKeys: d.SharePublicKeys, + C: d.C, + R: d.R, }, } diff --git a/gnark/circuits/aesV2_oprf/tables.go b/gnark/circuits/aesV2_oprf/tables.go deleted file mode 100644 index 14e4e93..0000000 --- a/gnark/circuits/aesV2_oprf/tables.go +++ /dev/null @@ -1,159 +0,0 @@ -package aes_v2_oprf - -import "github.com/consensys/gnark/frontend" - -var T = [4][256]frontend.Variable{ - { - 0xa56363c6, 0x847c7cf8, 0x997777ee, 0x8d7b7bf6, 0x0df2f2ff, 0xbd6b6bd6, 0xb16f6fde, 0x54c5c591, - 0x50303060, 0x03010102, 0xa96767ce, 0x7d2b2b56, 0x19fefee7, 0x62d7d7b5, 0xe6abab4d, 0x9a7676ec, - 0x45caca8f, 0x9d82821f, 0x40c9c989, 0x877d7dfa, 0x15fafaef, 0xeb5959b2, 0xc947478e, 0x0bf0f0fb, - 0xecadad41, 0x67d4d4b3, 0xfda2a25f, 0xeaafaf45, 0xbf9c9c23, 0xf7a4a453, 0x967272e4, 0x5bc0c09b, - 0xc2b7b775, 0x1cfdfde1, 0xae93933d, 0x6a26264c, 0x5a36366c, 0x413f3f7e, 0x02f7f7f5, 0x4fcccc83, - 0x5c343468, 0xf4a5a551, 0x34e5e5d1, 0x08f1f1f9, 0x937171e2, 0x73d8d8ab, 0x53313162, 0x3f15152a, - 0x0c040408, 0x52c7c795, 0x65232346, 0x5ec3c39d, 0x28181830, 0xa1969637, 0x0f05050a, 0xb59a9a2f, - 0x0907070e, 0x36121224, 0x9b80801b, 0x3de2e2df, 0x26ebebcd, 0x6927274e, 0xcdb2b27f, 0x9f7575ea, - 0x1b090912, 0x9e83831d, 0x742c2c58, 0x2e1a1a34, 0x2d1b1b36, 0xb26e6edc, 0xee5a5ab4, 0xfba0a05b, - 0xf65252a4, 0x4d3b3b76, 0x61d6d6b7, 0xceb3b37d, 0x7b292952, 0x3ee3e3dd, 0x712f2f5e, 0x97848413, - 0xf55353a6, 0x68d1d1b9, 0x00000000, 0x2cededc1, 0x60202040, 0x1ffcfce3, 0xc8b1b179, 0xed5b5bb6, - 0xbe6a6ad4, 0x46cbcb8d, 0xd9bebe67, 0x4b393972, 0xde4a4a94, 0xd44c4c98, 0xe85858b0, 0x4acfcf85, - 0x6bd0d0bb, 0x2aefefc5, 0xe5aaaa4f, 0x16fbfbed, 0xc5434386, 0xd74d4d9a, 0x55333366, 0x94858511, - 0xcf45458a, 0x10f9f9e9, 0x06020204, 0x817f7ffe, 0xf05050a0, 0x443c3c78, 0xba9f9f25, 0xe3a8a84b, - 0xf35151a2, 0xfea3a35d, 0xc0404080, 0x8a8f8f05, 0xad92923f, 0xbc9d9d21, 0x48383870, 0x04f5f5f1, - 0xdfbcbc63, 0xc1b6b677, 0x75dadaaf, 0x63212142, 0x30101020, 0x1affffe5, 0x0ef3f3fd, 0x6dd2d2bf, - 0x4ccdcd81, 0x140c0c18, 0x35131326, 0x2fececc3, 0xe15f5fbe, 0xa2979735, 0xcc444488, 0x3917172e, - 0x57c4c493, 0xf2a7a755, 0x827e7efc, 0x473d3d7a, 0xac6464c8, 0xe75d5dba, 0x2b191932, 0x957373e6, - 0xa06060c0, 0x98818119, 0xd14f4f9e, 0x7fdcdca3, 0x66222244, 0x7e2a2a54, 0xab90903b, 0x8388880b, - 0xca46468c, 0x29eeeec7, 0xd3b8b86b, 0x3c141428, 0x79dedea7, 0xe25e5ebc, 0x1d0b0b16, 0x76dbdbad, - 0x3be0e0db, 0x56323264, 0x4e3a3a74, 0x1e0a0a14, 0xdb494992, 0x0a06060c, 0x6c242448, 0xe45c5cb8, - 0x5dc2c29f, 0x6ed3d3bd, 0xefacac43, 0xa66262c4, 0xa8919139, 0xa4959531, 0x37e4e4d3, 0x8b7979f2, - 0x32e7e7d5, 0x43c8c88b, 0x5937376e, 0xb76d6dda, 0x8c8d8d01, 0x64d5d5b1, 0xd24e4e9c, 0xe0a9a949, - 0xb46c6cd8, 0xfa5656ac, 0x07f4f4f3, 0x25eaeacf, 0xaf6565ca, 0x8e7a7af4, 0xe9aeae47, 0x18080810, - 0xd5baba6f, 0x887878f0, 0x6f25254a, 0x722e2e5c, 0x241c1c38, 0xf1a6a657, 0xc7b4b473, 0x51c6c697, - 0x23e8e8cb, 0x7cdddda1, 0x9c7474e8, 0x211f1f3e, 0xdd4b4b96, 0xdcbdbd61, 0x868b8b0d, 0x858a8a0f, - 0x907070e0, 0x423e3e7c, 0xc4b5b571, 0xaa6666cc, 0xd8484890, 0x05030306, 0x01f6f6f7, 0x120e0e1c, - 0xa36161c2, 0x5f35356a, 0xf95757ae, 0xd0b9b969, 0x91868617, 0x58c1c199, 0x271d1d3a, 0xb99e9e27, - 0x38e1e1d9, 0x13f8f8eb, 0xb398982b, 0x33111122, 0xbb6969d2, 0x70d9d9a9, 0x898e8e07, 0xa7949433, - 0xb69b9b2d, 0x221e1e3c, 0x92878715, 0x20e9e9c9, 0x49cece87, 0xff5555aa, 0x78282850, 0x7adfdfa5, - 0x8f8c8c03, 0xf8a1a159, 0x80898909, 0x170d0d1a, 0xdabfbf65, 0x31e6e6d7, 0xc6424284, 0xb86868d0, - 0xc3414182, 0xb0999929, 0x772d2d5a, 0x110f0f1e, 0xcbb0b07b, 0xfc5454a8, 0xd6bbbb6d, 0x3a16162c, - }, { - 0x6363c6a5, 0x7c7cf884, 0x7777ee99, 0x7b7bf68d, 0xf2f2ff0d, 0x6b6bd6bd, 0x6f6fdeb1, 0xc5c59154, - 0x30306050, 0x01010203, 0x6767cea9, 0x2b2b567d, 0xfefee719, 0xd7d7b562, 0xabab4de6, 0x7676ec9a, - 0xcaca8f45, 0x82821f9d, 0xc9c98940, 0x7d7dfa87, 0xfafaef15, 0x5959b2eb, 0x47478ec9, 0xf0f0fb0b, - 0xadad41ec, 0xd4d4b367, 0xa2a25ffd, 0xafaf45ea, 0x9c9c23bf, 0xa4a453f7, 0x7272e496, 0xc0c09b5b, - 0xb7b775c2, 0xfdfde11c, 0x93933dae, 0x26264c6a, 0x36366c5a, 0x3f3f7e41, 0xf7f7f502, 0xcccc834f, - 0x3434685c, 0xa5a551f4, 0xe5e5d134, 0xf1f1f908, 0x7171e293, 0xd8d8ab73, 0x31316253, 0x15152a3f, - 0x0404080c, 0xc7c79552, 0x23234665, 0xc3c39d5e, 0x18183028, 0x969637a1, 0x05050a0f, 0x9a9a2fb5, - 0x07070e09, 0x12122436, 0x80801b9b, 0xe2e2df3d, 0xebebcd26, 0x27274e69, 0xb2b27fcd, 0x7575ea9f, - 0x0909121b, 0x83831d9e, 0x2c2c5874, 0x1a1a342e, 0x1b1b362d, 0x6e6edcb2, 0x5a5ab4ee, 0xa0a05bfb, - 0x5252a4f6, 0x3b3b764d, 0xd6d6b761, 0xb3b37dce, 0x2929527b, 0xe3e3dd3e, 0x2f2f5e71, 0x84841397, - 0x5353a6f5, 0xd1d1b968, 0x00000000, 0xededc12c, 0x20204060, 0xfcfce31f, 0xb1b179c8, 0x5b5bb6ed, - 0x6a6ad4be, 0xcbcb8d46, 0xbebe67d9, 0x3939724b, 0x4a4a94de, 0x4c4c98d4, 0x5858b0e8, 0xcfcf854a, - 0xd0d0bb6b, 0xefefc52a, 0xaaaa4fe5, 0xfbfbed16, 0x434386c5, 0x4d4d9ad7, 0x33336655, 0x85851194, - 0x45458acf, 0xf9f9e910, 0x02020406, 0x7f7ffe81, 0x5050a0f0, 0x3c3c7844, 0x9f9f25ba, 0xa8a84be3, - 0x5151a2f3, 0xa3a35dfe, 0x404080c0, 0x8f8f058a, 0x92923fad, 0x9d9d21bc, 0x38387048, 0xf5f5f104, - 0xbcbc63df, 0xb6b677c1, 0xdadaaf75, 0x21214263, 0x10102030, 0xffffe51a, 0xf3f3fd0e, 0xd2d2bf6d, - 0xcdcd814c, 0x0c0c1814, 0x13132635, 0xececc32f, 0x5f5fbee1, 0x979735a2, 0x444488cc, 0x17172e39, - 0xc4c49357, 0xa7a755f2, 0x7e7efc82, 0x3d3d7a47, 0x6464c8ac, 0x5d5dbae7, 0x1919322b, 0x7373e695, - 0x6060c0a0, 0x81811998, 0x4f4f9ed1, 0xdcdca37f, 0x22224466, 0x2a2a547e, 0x90903bab, 0x88880b83, - 0x46468cca, 0xeeeec729, 0xb8b86bd3, 0x1414283c, 0xdedea779, 0x5e5ebce2, 0x0b0b161d, 0xdbdbad76, - 0xe0e0db3b, 0x32326456, 0x3a3a744e, 0x0a0a141e, 0x494992db, 0x06060c0a, 0x2424486c, 0x5c5cb8e4, - 0xc2c29f5d, 0xd3d3bd6e, 0xacac43ef, 0x6262c4a6, 0x919139a8, 0x959531a4, 0xe4e4d337, 0x7979f28b, - 0xe7e7d532, 0xc8c88b43, 0x37376e59, 0x6d6ddab7, 0x8d8d018c, 0xd5d5b164, 0x4e4e9cd2, 0xa9a949e0, - 0x6c6cd8b4, 0x5656acfa, 0xf4f4f307, 0xeaeacf25, 0x6565caaf, 0x7a7af48e, 0xaeae47e9, 0x08081018, - 0xbaba6fd5, 0x7878f088, 0x25254a6f, 0x2e2e5c72, 0x1c1c3824, 0xa6a657f1, 0xb4b473c7, 0xc6c69751, - 0xe8e8cb23, 0xdddda17c, 0x7474e89c, 0x1f1f3e21, 0x4b4b96dd, 0xbdbd61dc, 0x8b8b0d86, 0x8a8a0f85, - 0x7070e090, 0x3e3e7c42, 0xb5b571c4, 0x6666ccaa, 0x484890d8, 0x03030605, 0xf6f6f701, 0x0e0e1c12, - 0x6161c2a3, 0x35356a5f, 0x5757aef9, 0xb9b969d0, 0x86861791, 0xc1c19958, 0x1d1d3a27, 0x9e9e27b9, - 0xe1e1d938, 0xf8f8eb13, 0x98982bb3, 0x11112233, 0x6969d2bb, 0xd9d9a970, 0x8e8e0789, 0x949433a7, - 0x9b9b2db6, 0x1e1e3c22, 0x87871592, 0xe9e9c920, 0xcece8749, 0x5555aaff, 0x28285078, 0xdfdfa57a, - 0x8c8c038f, 0xa1a159f8, 0x89890980, 0x0d0d1a17, 0xbfbf65da, 0xe6e6d731, 0x424284c6, 0x6868d0b8, - 0x414182c3, 0x999929b0, 0x2d2d5a77, 0x0f0f1e11, 0xb0b07bcb, 0x5454a8fc, 0xbbbb6dd6, 0x16162c3a, - }, { - 0x63c6a563, 0x7cf8847c, 0x77ee9977, 0x7bf68d7b, 0xf2ff0df2, 0x6bd6bd6b, 0x6fdeb16f, 0xc59154c5, - 0x30605030, 0x01020301, 0x67cea967, 0x2b567d2b, 0xfee719fe, 0xd7b562d7, 0xab4de6ab, 0x76ec9a76, - 0xca8f45ca, 0x821f9d82, 0xc98940c9, 0x7dfa877d, 0xfaef15fa, 0x59b2eb59, 0x478ec947, 0xf0fb0bf0, - 0xad41ecad, 0xd4b367d4, 0xa25ffda2, 0xaf45eaaf, 0x9c23bf9c, 0xa453f7a4, 0x72e49672, 0xc09b5bc0, - 0xb775c2b7, 0xfde11cfd, 0x933dae93, 0x264c6a26, 0x366c5a36, 0x3f7e413f, 0xf7f502f7, 0xcc834fcc, - 0x34685c34, 0xa551f4a5, 0xe5d134e5, 0xf1f908f1, 0x71e29371, 0xd8ab73d8, 0x31625331, 0x152a3f15, - 0x04080c04, 0xc79552c7, 0x23466523, 0xc39d5ec3, 0x18302818, 0x9637a196, 0x050a0f05, 0x9a2fb59a, - 0x070e0907, 0x12243612, 0x801b9b80, 0xe2df3de2, 0xebcd26eb, 0x274e6927, 0xb27fcdb2, 0x75ea9f75, - 0x09121b09, 0x831d9e83, 0x2c58742c, 0x1a342e1a, 0x1b362d1b, 0x6edcb26e, 0x5ab4ee5a, 0xa05bfba0, - 0x52a4f652, 0x3b764d3b, 0xd6b761d6, 0xb37dceb3, 0x29527b29, 0xe3dd3ee3, 0x2f5e712f, 0x84139784, - 0x53a6f553, 0xd1b968d1, 0x00000000, 0xedc12ced, 0x20406020, 0xfce31ffc, 0xb179c8b1, 0x5bb6ed5b, - 0x6ad4be6a, 0xcb8d46cb, 0xbe67d9be, 0x39724b39, 0x4a94de4a, 0x4c98d44c, 0x58b0e858, 0xcf854acf, - 0xd0bb6bd0, 0xefc52aef, 0xaa4fe5aa, 0xfbed16fb, 0x4386c543, 0x4d9ad74d, 0x33665533, 0x85119485, - 0x458acf45, 0xf9e910f9, 0x02040602, 0x7ffe817f, 0x50a0f050, 0x3c78443c, 0x9f25ba9f, 0xa84be3a8, - 0x51a2f351, 0xa35dfea3, 0x4080c040, 0x8f058a8f, 0x923fad92, 0x9d21bc9d, 0x38704838, 0xf5f104f5, - 0xbc63dfbc, 0xb677c1b6, 0xdaaf75da, 0x21426321, 0x10203010, 0xffe51aff, 0xf3fd0ef3, 0xd2bf6dd2, - 0xcd814ccd, 0x0c18140c, 0x13263513, 0xecc32fec, 0x5fbee15f, 0x9735a297, 0x4488cc44, 0x172e3917, - 0xc49357c4, 0xa755f2a7, 0x7efc827e, 0x3d7a473d, 0x64c8ac64, 0x5dbae75d, 0x19322b19, 0x73e69573, - 0x60c0a060, 0x81199881, 0x4f9ed14f, 0xdca37fdc, 0x22446622, 0x2a547e2a, 0x903bab90, 0x880b8388, - 0x468cca46, 0xeec729ee, 0xb86bd3b8, 0x14283c14, 0xdea779de, 0x5ebce25e, 0x0b161d0b, 0xdbad76db, - 0xe0db3be0, 0x32645632, 0x3a744e3a, 0x0a141e0a, 0x4992db49, 0x060c0a06, 0x24486c24, 0x5cb8e45c, - 0xc29f5dc2, 0xd3bd6ed3, 0xac43efac, 0x62c4a662, 0x9139a891, 0x9531a495, 0xe4d337e4, 0x79f28b79, - 0xe7d532e7, 0xc88b43c8, 0x376e5937, 0x6ddab76d, 0x8d018c8d, 0xd5b164d5, 0x4e9cd24e, 0xa949e0a9, - 0x6cd8b46c, 0x56acfa56, 0xf4f307f4, 0xeacf25ea, 0x65caaf65, 0x7af48e7a, 0xae47e9ae, 0x08101808, - 0xba6fd5ba, 0x78f08878, 0x254a6f25, 0x2e5c722e, 0x1c38241c, 0xa657f1a6, 0xb473c7b4, 0xc69751c6, - 0xe8cb23e8, 0xdda17cdd, 0x74e89c74, 0x1f3e211f, 0x4b96dd4b, 0xbd61dcbd, 0x8b0d868b, 0x8a0f858a, - 0x70e09070, 0x3e7c423e, 0xb571c4b5, 0x66ccaa66, 0x4890d848, 0x03060503, 0xf6f701f6, 0x0e1c120e, - 0x61c2a361, 0x356a5f35, 0x57aef957, 0xb969d0b9, 0x86179186, 0xc19958c1, 0x1d3a271d, 0x9e27b99e, - 0xe1d938e1, 0xf8eb13f8, 0x982bb398, 0x11223311, 0x69d2bb69, 0xd9a970d9, 0x8e07898e, 0x9433a794, - 0x9b2db69b, 0x1e3c221e, 0x87159287, 0xe9c920e9, 0xce8749ce, 0x55aaff55, 0x28507828, 0xdfa57adf, - 0x8c038f8c, 0xa159f8a1, 0x89098089, 0x0d1a170d, 0xbf65dabf, 0xe6d731e6, 0x4284c642, 0x68d0b868, - 0x4182c341, 0x9929b099, 0x2d5a772d, 0x0f1e110f, 0xb07bcbb0, 0x54a8fc54, 0xbb6dd6bb, 0x162c3a16, - }, { - 0xc6a56363, 0xf8847c7c, 0xee997777, 0xf68d7b7b, 0xff0df2f2, 0xd6bd6b6b, 0xdeb16f6f, 0x9154c5c5, - 0x60503030, 0x02030101, 0xcea96767, 0x567d2b2b, 0xe719fefe, 0xb562d7d7, 0x4de6abab, 0xec9a7676, - 0x8f45caca, 0x1f9d8282, 0x8940c9c9, 0xfa877d7d, 0xef15fafa, 0xb2eb5959, 0x8ec94747, 0xfb0bf0f0, - 0x41ecadad, 0xb367d4d4, 0x5ffda2a2, 0x45eaafaf, 0x23bf9c9c, 0x53f7a4a4, 0xe4967272, 0x9b5bc0c0, - 0x75c2b7b7, 0xe11cfdfd, 0x3dae9393, 0x4c6a2626, 0x6c5a3636, 0x7e413f3f, 0xf502f7f7, 0x834fcccc, - 0x685c3434, 0x51f4a5a5, 0xd134e5e5, 0xf908f1f1, 0xe2937171, 0xab73d8d8, 0x62533131, 0x2a3f1515, - 0x080c0404, 0x9552c7c7, 0x46652323, 0x9d5ec3c3, 0x30281818, 0x37a19696, 0x0a0f0505, 0x2fb59a9a, - 0x0e090707, 0x24361212, 0x1b9b8080, 0xdf3de2e2, 0xcd26ebeb, 0x4e692727, 0x7fcdb2b2, 0xea9f7575, - 0x121b0909, 0x1d9e8383, 0x58742c2c, 0x342e1a1a, 0x362d1b1b, 0xdcb26e6e, 0xb4ee5a5a, 0x5bfba0a0, - 0xa4f65252, 0x764d3b3b, 0xb761d6d6, 0x7dceb3b3, 0x527b2929, 0xdd3ee3e3, 0x5e712f2f, 0x13978484, - 0xa6f55353, 0xb968d1d1, 0x00000000, 0xc12ceded, 0x40602020, 0xe31ffcfc, 0x79c8b1b1, 0xb6ed5b5b, - 0xd4be6a6a, 0x8d46cbcb, 0x67d9bebe, 0x724b3939, 0x94de4a4a, 0x98d44c4c, 0xb0e85858, 0x854acfcf, - 0xbb6bd0d0, 0xc52aefef, 0x4fe5aaaa, 0xed16fbfb, 0x86c54343, 0x9ad74d4d, 0x66553333, 0x11948585, - 0x8acf4545, 0xe910f9f9, 0x04060202, 0xfe817f7f, 0xa0f05050, 0x78443c3c, 0x25ba9f9f, 0x4be3a8a8, - 0xa2f35151, 0x5dfea3a3, 0x80c04040, 0x058a8f8f, 0x3fad9292, 0x21bc9d9d, 0x70483838, 0xf104f5f5, - 0x63dfbcbc, 0x77c1b6b6, 0xaf75dada, 0x42632121, 0x20301010, 0xe51affff, 0xfd0ef3f3, 0xbf6dd2d2, - 0x814ccdcd, 0x18140c0c, 0x26351313, 0xc32fecec, 0xbee15f5f, 0x35a29797, 0x88cc4444, 0x2e391717, - 0x9357c4c4, 0x55f2a7a7, 0xfc827e7e, 0x7a473d3d, 0xc8ac6464, 0xbae75d5d, 0x322b1919, 0xe6957373, - 0xc0a06060, 0x19988181, 0x9ed14f4f, 0xa37fdcdc, 0x44662222, 0x547e2a2a, 0x3bab9090, 0x0b838888, - 0x8cca4646, 0xc729eeee, 0x6bd3b8b8, 0x283c1414, 0xa779dede, 0xbce25e5e, 0x161d0b0b, 0xad76dbdb, - 0xdb3be0e0, 0x64563232, 0x744e3a3a, 0x141e0a0a, 0x92db4949, 0x0c0a0606, 0x486c2424, 0xb8e45c5c, - 0x9f5dc2c2, 0xbd6ed3d3, 0x43efacac, 0xc4a66262, 0x39a89191, 0x31a49595, 0xd337e4e4, 0xf28b7979, - 0xd532e7e7, 0x8b43c8c8, 0x6e593737, 0xdab76d6d, 0x018c8d8d, 0xb164d5d5, 0x9cd24e4e, 0x49e0a9a9, - 0xd8b46c6c, 0xacfa5656, 0xf307f4f4, 0xcf25eaea, 0xcaaf6565, 0xf48e7a7a, 0x47e9aeae, 0x10180808, - 0x6fd5baba, 0xf0887878, 0x4a6f2525, 0x5c722e2e, 0x38241c1c, 0x57f1a6a6, 0x73c7b4b4, 0x9751c6c6, - 0xcb23e8e8, 0xa17cdddd, 0xe89c7474, 0x3e211f1f, 0x96dd4b4b, 0x61dcbdbd, 0x0d868b8b, 0x0f858a8a, - 0xe0907070, 0x7c423e3e, 0x71c4b5b5, 0xccaa6666, 0x90d84848, 0x06050303, 0xf701f6f6, 0x1c120e0e, - 0xc2a36161, 0x6a5f3535, 0xaef95757, 0x69d0b9b9, 0x17918686, 0x9958c1c1, 0x3a271d1d, 0x27b99e9e, - 0xd938e1e1, 0xeb13f8f8, 0x2bb39898, 0x22331111, 0xd2bb6969, 0xa970d9d9, 0x07898e8e, 0x33a79494, - 0x2db69b9b, 0x3c221e1e, 0x15928787, 0xc920e9e9, 0x8749cece, 0xaaff5555, 0x50782828, 0xa57adfdf, - 0x038f8c8c, 0x59f8a1a1, 0x09808989, 0x1a170d0d, 0x65dabfbf, 0xd731e6e6, 0x84c64242, 0xd0b86868, - 0x82c34141, 0x29b09999, 0x5a772d2d, 0x1e110f0f, 0x7bcbb0b0, 0xa8fc5454, 0x6dd6bbbb, 0x2c3a1616, - }} - -var sbox0 = [256]frontend.Variable{ - 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76, - 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0, - 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15, - 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75, - 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84, - 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf, - 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8, - 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2, - 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73, - 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb, - 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79, - 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08, - 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a, - 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e, - 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf, - 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16, -} - -var byte_order = []int{0, 5, 10, 15, 4, 9, 14, 3, 8, 13, 2, 7, 12, 1, 6, 11} diff --git a/gnark/circuits/chachaV3/circuit.go b/gnark/circuits/chachaV3/circuit.go index 9c14746..099d6ce 100644 --- a/gnark/circuits/chachaV3/circuit.go +++ b/gnark/circuits/chachaV3/circuit.go @@ -1,69 +1,17 @@ package chachaV3 -import ( - "gnark-symmetric-crypto/utils" - - "github.com/consensys/gnark/frontend" -) - -const Blocks = 2 +import "github.com/consensys/gnark/frontend" type ChaChaCircuit struct { - Key [8][BITS_PER_WORD]frontend.Variable - Counter [BITS_PER_WORD]frontend.Variable `gnark:",public"` - Nonce [3][BITS_PER_WORD]frontend.Variable `gnark:",public"` - In [16 * Blocks][BITS_PER_WORD]frontend.Variable `gnark:",public"` - Out [16 * Blocks][BITS_PER_WORD]frontend.Variable `gnark:",public"` + ChaChaBaseCircuit + Out [16 * Blocks][BITS_PER_WORD]frontend.Variable `gnark:",public"` } func (c *ChaChaCircuit) Define(api frontend.API) error { - var state [16][BITS_PER_WORD]frontend.Variable - counter := c.Counter - - var one [BITS_PER_WORD]frontend.Variable - copy(one[:], api.ToBinary(1, 32)) - - c1 := utils.Uint32ToBits(0x61707865) - c2 := utils.Uint32ToBits(0x3320646e) - c3 := utils.Uint32ToBits(0x79622d32) - c4 := utils.Uint32ToBits(0x6b206574) - for b := 0; b < Blocks; b++ { - // Fill state. Start with constants - - copy(state[0][:], c1[:]) - copy(state[1][:], c2[:]) - copy(state[2][:], c3[:]) - copy(state[3][:], c4[:]) - - // set key - copy(state[4:], c.Key[:]) - // set counter - state[12] = counter - // set nonce - copy(state[13:], c.Nonce[:]) - // modify state with round function - Round(api, &state) - // produce keystream from state - Serialize(&state) - - // xor keystream with input - var output [16][BITS_PER_WORD]frontend.Variable - for i, s := range state { - xor32(api, &c.In[b*16+i], &s, &output[i]) - } - - // check that output matches ciphertext - for i := 0; i < 16; i++ { - for j := 0; j < BITS_PER_WORD; j++ { - api.AssertIsEqual(c.Out[b*16+i][j], output[i][j]) - } - } - // increment counter for next block - if b+1 < Blocks { - add32(api, &counter, &one) - } + err := c.ChaChaBaseCircuit.Define(api, c.Out) + if err != nil { + return err } - return nil } diff --git a/gnark/circuits/chachaV3/common.go b/gnark/circuits/chachaV3/common.go new file mode 100644 index 0000000..676c59f --- /dev/null +++ b/gnark/circuits/chachaV3/common.go @@ -0,0 +1,67 @@ +package chachaV3 + +import ( + "github.com/consensys/gnark/frontend" + "github.com/consensys/gnark/std/math/bits" +) + +const Blocks = 2 + +type ChaChaBaseCircuit struct { + Key [8][BITS_PER_WORD]frontend.Variable + Counter [BITS_PER_WORD]frontend.Variable `gnark:",public"` + Nonce [3][BITS_PER_WORD]frontend.Variable `gnark:",public"` + In [16 * Blocks][BITS_PER_WORD]frontend.Variable `gnark:",public"` +} + +func (c *ChaChaBaseCircuit) Define(api frontend.API, out [16 * Blocks][BITS_PER_WORD]frontend.Variable) error { + + var state [16][BITS_PER_WORD]frontend.Variable + counter := c.Counter + + var one [BITS_PER_WORD]frontend.Variable + copy(one[:], api.ToBinary(1, 32)) + + c1 := bits.ToBinary(api, 0x61707865, bits.WithNbDigits(32)) + c2 := bits.ToBinary(api, 0x3320646e, bits.WithNbDigits(32)) + c3 := bits.ToBinary(api, 0x79622d32, bits.WithNbDigits(32)) + c4 := bits.ToBinary(api, 0x6b206574, bits.WithNbDigits(32)) + for b := 0; b < Blocks; b++ { + // Fill state. Start with constants + + copy(state[0][:], c1[:]) + copy(state[1][:], c2[:]) + copy(state[2][:], c3[:]) + copy(state[3][:], c4[:]) + + // set key + copy(state[4:], c.Key[:]) + // set counter + state[12] = counter + // set nonce + copy(state[13:], c.Nonce[:]) + // modify state with round function + Round(api, &state) + // produce keystream from state + Serialize(&state) + + // xor keystream with input + var output [16][BITS_PER_WORD]frontend.Variable + for i, s := range state { + xor32(api, &c.In[b*16+i], &s, &output[i]) + } + + // check that output matches ciphertext + for i := 0; i < 16; i++ { + for j := 0; j < BITS_PER_WORD; j++ { + api.AssertIsEqual(out[b*16+i][j], output[i][j]) + } + } + // increment counter for next block + if b+1 < Blocks { + add32(api, &counter, &one) + } + } + + return nil +} diff --git a/gnark/circuits/chachaV3_oprf/chacha_test.go b/gnark/circuits/chachaV3_oprf/chacha_test.go index f39bb1f..2bd97d9 100644 --- a/gnark/circuits/chachaV3_oprf/chacha_test.go +++ b/gnark/circuits/chachaV3_oprf/chacha_test.go @@ -3,6 +3,7 @@ package chachaV3_oprf import ( "crypto/rand" "fmt" + "gnark-symmetric-crypto/circuits/chachaV3" "gnark-symmetric-crypto/circuits/toprf" "gnark-symmetric-crypto/utils" "testing" @@ -16,112 +17,6 @@ import ( "golang.org/x/crypto/chacha20" ) -type qrBlock struct { - In [16][BITS_PER_WORD]frontend.Variable - Out [16][BITS_PER_WORD]frontend.Variable `gnark:",public"` -} - -func (c *qrBlock) Define(api frontend.API) error { - - a0 := api.ToBinary(0x11111111) - a1 := api.ToBinary(0x01020304) - a2 := api.ToBinary(0x9b8d6f43) - a3 := api.ToBinary(0x01234567) - - b0 := api.ToBinary(0xea2a92f4) - b1 := api.ToBinary(0xcb1cf8ce) - b2 := api.ToBinary(0x4581472e) - b3 := api.ToBinary(0x5881c4bb) - - for i := 0; i < BITS_PER_WORD; i++ { - c.In[0][i] = a0[i] - c.In[1][i] = a1[i] - c.In[2][i] = a2[i] - c.In[3][i] = a3[i] - - c.Out[0][i] = b0[i] - c.Out[1][i] = b1[i] - c.Out[2][i] = b2[i] - c.Out[3][i] = b3[i] - } - - QR(api, &c.In, 0, 1, 2, 3) - for i := range c.Out { - a := api.FromBinary(c.In[i][:]...) - b := api.FromBinary(c.Out[i][:]...) - api.AssertIsEqual(a, b) - - } - return nil -} - -func TestQR(t *testing.T) { - assert := test.NewAssert(t) - witness := qrBlock{} - for i := 0; i < 16; i++ { - witness.In[i] = [BITS_PER_WORD]frontend.Variable{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, 0, 0, 0} - witness.Out[i] = [BITS_PER_WORD]frontend.Variable{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, 0, 0, 0} - } - - err := test.IsSolved(&qrBlock{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - - assert.CheckCircuit(&qrBlock{}, test.WithValidAssignment(&witness)) - -} - -type roundCircuit struct { - In [16][BITS_PER_WORD]frontend.Variable - Out [16][BITS_PER_WORD]frontend.Variable `gnark:",public"` -} - -func (c *roundCircuit) Define(api frontend.API) error { - - var workingState [16][BITS_PER_WORD]frontend.Variable - copy(workingState[:], c.In[:]) - - Round(api, &workingState) - Serialize(&workingState) - - for i := range c.Out { - - a := api.FromBinary(c.Out[i][:]...) - b := api.FromBinary(workingState[i][:]...) - api.AssertIsEqual(a, b) - } - - return nil -} - -func TestRound(t *testing.T) { - assert := test.NewAssert(t) - - in := []frontend.Variable{ - 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574, - 0x03020100, 0x07060504, 0x0b0a0908, 0x0f0e0d0c, - 0x13121110, 0x17161514, 0x1b1a1918, 0x1f1e1d1c, - 0x00000001, 0x09000000, 0x4a000000, 0x00000000} - - out := utils.BytesToUint32BERaw([]uint8{ - 0x10, 0xf1, 0xe7, 0xe4, 0xd1, 0x3b, 0x59, 0x15, 0x50, 0x0f, 0xdd, 0x1f, 0xa3, 0x20, 0x71, 0xc4, - 0xc7, 0xd1, 0xf4, 0xc7, 0x33, 0xc0, 0x68, 0x03, 0x04, 0x22, 0xaa, 0x9a, 0xc3, 0xd4, 0x6c, 0x4e, - 0xd2, 0x82, 0x64, 0x46, 0x07, 0x9f, 0xaa, 0x09, 0x14, 0xc2, 0xd7, 0x05, 0xd9, 0x8b, 0x02, 0xa2, - 0xb5, 0x12, 0x9c, 0xd1, 0xde, 0x16, 0x4e, 0xb9, 0xcb, 0xd0, 0x83, 0xe8, 0xa2, 0x50, 0x3c, 0x4e}) - - witness := roundCircuit{} - - for i := 0; i < len(in); i++ { - a := utils.Uint32ToBits(in[i]) - b := utils.Uint32ToBits(out[i]) - copy(witness.In[i][:], a[:]) - copy(witness.Out[i][:], b[:]) - } - err := test.IsSolved(&roundCircuit{}, &witness, ecc.BN254.ScalarField()) - assert.NoError(err) - - assert.CheckCircuit(&roundCircuit{}, test.WithValidAssignment(&witness)) -} - func TestCipher(t *testing.T) { assert := test.NewAssert(t) @@ -135,10 +30,10 @@ func TestCipher(t *testing.T) { pos := 128 - 62 counter := 12345 - plaintext := make([]byte, Blocks*64) + plaintext := make([]byte, chachaV3.Blocks*64) copy(plaintext[pos:], secretBytes) - ciphertext := make([]byte, Blocks*64) + ciphertext := make([]byte, chachaV3.Blocks*64) cipher, err := chacha20.NewUnauthenticatedCipher(bKey, bNonce) assert.NoError(err) @@ -175,18 +70,19 @@ func TestCipher(t *testing.T) { assert.NoError(err) } -func createWitness(d *toprf.TOPRFParams, bKey []uint8, bNonce []uint8, counter int, ciphertext []byte, plaintext []byte, pos, len int) ChachaTOPRFCircuit { +func createWitness(d *toprf.Params, bKey []uint8, bNonce []uint8, counter int, ciphertext []byte, plaintext []byte, pos, len int) ChachaTOPRFCircuit { witness := ChachaTOPRFCircuit{ Len: len, - TOPRF: TOPRFData{ - Mask: d.Mask, - DomainSeparator: d.DomainSeparator, - EvaluatedElements: d.Responses, - Coefficients: d.Coefficients, - Output: d.Output, - PublicKeys: d.SharePublicKeys, - C: d.C, - R: d.R, + TOPRF: toprf.Params{ + SecretData: [2]frontend.Variable{0, 0}, // will be rewritten inside + Mask: d.Mask, + DomainSeparator: d.DomainSeparator, + Responses: d.Responses, + Coefficients: d.Coefficients, + Output: d.Output, + SharePublicKeys: d.SharePublicKeys, + C: d.C, + R: d.R, }, } diff --git a/gnark/circuits/chachaV3_oprf/circuit.go b/gnark/circuits/chachaV3_oprf/circuit.go index c40e1e3..5f16626 100644 --- a/gnark/circuits/chachaV3_oprf/circuit.go +++ b/gnark/circuits/chachaV3_oprf/circuit.go @@ -1,96 +1,31 @@ package chachaV3_oprf import ( + "gnark-symmetric-crypto/circuits/chachaV3" "gnark-symmetric-crypto/circuits/toprf" - "math/big" "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/algebra/native/twistededwards" - "github.com/consensys/gnark/std/math/bits" - "github.com/consensys/gnark/std/math/cmp" ) -const Blocks = 2 -const BytesPerElement = 31 - -type TOPRFData struct { - DomainSeparator frontend.Variable `gnark:",public"` - Mask frontend.Variable - - EvaluatedElements [toprf.Threshold]twistededwards.Point `gnark:",public"` // responses per each node - Coefficients [toprf.Threshold]frontend.Variable `gnark:",public"` // coeffs for reconstructing element - - // Proofs of DLEQ per node - PublicKeys [toprf.Threshold]twistededwards.Point `gnark:",public"` - C [toprf.Threshold]frontend.Variable `gnark:",public"` - R [toprf.Threshold]frontend.Variable `gnark:",public"` - - Output frontend.Variable `gnark:",public"` -} +const BITS_PER_WORD = 32 +const TOTAL_BITS = 16 * chachaV3.Blocks * BITS_PER_WORD type ChachaTOPRFCircuit struct { - Key [8][BITS_PER_WORD]frontend.Variable - Counter [BITS_PER_WORD]frontend.Variable `gnark:",public"` - Nonce [3][BITS_PER_WORD]frontend.Variable `gnark:",public"` - In [16 * Blocks][BITS_PER_WORD]frontend.Variable `gnark:",public"` // ciphertext - Out [16 * Blocks][BITS_PER_WORD]frontend.Variable // plaintext - Bitmask [16 * Blocks * BITS_PER_WORD]frontend.Variable `gnark:",public"` // bit mask for bits being hashed - - // Length of "secret data" elements to be hashed. In bytes - Len frontend.Variable `gnark:",public"` + chachaV3.ChaChaBaseCircuit + Out [16 * chachaV3.Blocks][BITS_PER_WORD]frontend.Variable // plaintext + Bitmask [TOTAL_BITS]frontend.Variable `gnark:",public"` // bit mask for bits being hashed + Len frontend.Variable `gnark:",public"` // Length of "secret data" elements to be hashed. In bytes - TOPRF TOPRFData + TOPRF toprf.Params } func (c *ChachaTOPRFCircuit) Define(api frontend.API) error { - outBits := make([]frontend.Variable, 16*Blocks*BITS_PER_WORD) - var state [16][BITS_PER_WORD]frontend.Variable - counter := c.Counter - - var one [BITS_PER_WORD]frontend.Variable - copy(one[:], api.ToBinary(1, 32)) - - c1 := bits.ToBinary(api, 0x61707865, bits.WithNbDigits(32)) - c2 := bits.ToBinary(api, 0x3320646e, bits.WithNbDigits(32)) - c3 := bits.ToBinary(api, 0x79622d32, bits.WithNbDigits(32)) - c4 := bits.ToBinary(api, 0x6b206574, bits.WithNbDigits(32)) - for b := 0; b < Blocks; b++ { - // Fill state. Start with constants - - copy(state[0][:], c1[:]) - copy(state[1][:], c2[:]) - copy(state[2][:], c3[:]) - copy(state[3][:], c4[:]) - - // set key - copy(state[4:], c.Key[:]) - // set counter - state[12] = counter - // set nonce - copy(state[13:], c.Nonce[:]) - // modify state with round function - Round(api, &state) - // produce keystream from state - Serialize(&state) - - // xor keystream with input - var output [16][BITS_PER_WORD]frontend.Variable - for i, s := range state { - xor32(api, &c.In[b*16+i], &s, &output[i]) - } - - // check that output matches calculated output - for i := 0; i < 16; i++ { - for j := 0; j < BITS_PER_WORD; j++ { - api.AssertIsEqual(c.Out[b*16+i][j], output[i][j]) - } - } - // increment counter for next block - if b+1 < Blocks { - add32(api, &counter, &one) - } + err := c.ChaChaBaseCircuit.Define(api, c.Out) + if err != nil { + return err } + outBits := make([]frontend.Variable, TOTAL_BITS) // flatten result bits array for i := 0; i < len(c.Out); i++ { word := i * 32 @@ -100,49 +35,7 @@ func (c *ChachaTOPRFCircuit) Define(api frontend.API) error { } } - pow1 := frontend.Variable(1) - pow2 := frontend.Variable(0) - res1 := frontend.Variable(0) - res2 := frontend.Variable(0) - totalBits := frontend.Variable(0) - - for i := 0; i < 16*Blocks*BITS_PER_WORD; i++ { - bitIndex := i - bitIsSet := c.Bitmask[bitIndex] - bit := api.Select(bitIsSet, outBits[bitIndex], 0) - - res1 = api.Add(res1, api.Mul(bit, pow1)) - res2 = api.Add(res2, api.Mul(bit, pow2)) - - n := api.Add(bitIsSet, 1) // do we need to multiply power by 2? - pow2 = api.Mul(pow2, n) - pow1 = api.Mul(pow1, n) - - totalBits = api.Add(totalBits, bitIsSet) - - r1Done := api.IsZero(api.Sub(totalBits, BytesPerElement*8)) // are we done with 1st number? - pow1 = api.Mul(pow1, api.Sub(1, r1Done)) // set pow1 to zero if yes - pow2 = api.Add(pow2, r1Done) // set pow2 to 1 to start increasing - - } - - api.AssertIsDifferent(c.Len, 0) // Len != 0 - - comparator := cmp.NewBoundedComparator(api, big.NewInt(16*Blocks*BITS_PER_WORD), false) // max diff is number of bits - comparator.AssertIsLessEq(totalBits, BytesPerElement*8*2) // check that number of processed bits <= 62 bytes - api.AssertIsEqual(totalBits, api.Mul(c.Len, 8)) // and that it corresponds to Len + c.TOPRF.SecretData = toprf.ExtractSecretElements(api, outBits, c.Bitmask[:], c.Len) - // check that TOPRF output was created from secret data by a server with a specific public key - oprfData := &toprf.TOPRFParams{ - SecretData: [2]frontend.Variable{res1, res2}, - DomainSeparator: c.TOPRF.DomainSeparator, - Mask: c.TOPRF.Mask, - Responses: c.TOPRF.EvaluatedElements, - Coefficients: c.TOPRF.Coefficients, - Output: c.TOPRF.Output, - SharePublicKeys: c.TOPRF.PublicKeys, - C: c.TOPRF.C, - R: c.TOPRF.R, - } - return toprf.VerifyTOPRF(api, oprfData) + return toprf.VerifyTOPRF(api, &c.TOPRF) } diff --git a/gnark/circuits/chachaV3_oprf/round.go b/gnark/circuits/chachaV3_oprf/round.go deleted file mode 100644 index 317bc71..0000000 --- a/gnark/circuits/chachaV3_oprf/round.go +++ /dev/null @@ -1,97 +0,0 @@ -package chachaV3_oprf - -import ( - "github.com/consensys/gnark/frontend" - "github.com/consensys/gnark/std/math/bits" -) - -const BITS_PER_WORD = 32 - -func QR(api frontend.API, state *[16][BITS_PER_WORD]frontend.Variable, i, j, k, l int) { - a, b, c, d := state[i], state[j], state[k], state[l] - - add32(api, &a, &b) - xorRot32(api, &d, &a, 16) - - add32(api, &c, &d) - xorRot32(api, &b, &c, 12) - - add32(api, &a, &b) - xorRot32(api, &d, &a, 8) - - add32(api, &c, &d) - xorRot32(api, &b, &c, 7) - - state[i] = a - state[j] = b - state[k] = c - state[l] = d -} - -func add32(api frontend.API, aBits, bBits *[BITS_PER_WORD]frontend.Variable) { - a := bits.FromBinary(api, aBits[:], bits.WithNbDigits(32)) - b := bits.FromBinary(api, bBits[:], bits.WithNbDigits(32)) - res := api.Add(a, b) - resBits := bits.ToBinary(api, res, bits.WithNbDigits(BITS_PER_WORD+1)) - for i := 0; i < BITS_PER_WORD; i++ { - aBits[i] = resBits[i] // api.Mul(resBits[i], 1) - } -} - -func xor32(api frontend.API, a, b, c *[BITS_PER_WORD]frontend.Variable) { - for i := 0; i < BITS_PER_WORD; i++ { - c[i] = api.Xor(a[i], b[i]) - } -} - -func xorRot32(api frontend.API, a, b *[BITS_PER_WORD]frontend.Variable, l int) { - var res [BITS_PER_WORD]frontend.Variable - for i := 0; i < BITS_PER_WORD; i++ { - iRot := (i + l) % BITS_PER_WORD - res[iRot] = api.Xor(a[i], b[i]) - } - for i := 0; i < BITS_PER_WORD; i++ { - a[i] = res[i] // api.Mul(res[i], 1) - } -} - -func Round(api frontend.API, state *[16][BITS_PER_WORD]frontend.Variable) { - var workingState [16][BITS_PER_WORD]frontend.Variable - copy(workingState[:], state[:]) - for i := 0; i < 10; i++ { - QR(api, &workingState, 0, 4, 8, 12) - QR(api, &workingState, 1, 5, 9, 13) - QR(api, &workingState, 2, 6, 10, 14) - QR(api, &workingState, 3, 7, 11, 15) - QR(api, &workingState, 0, 5, 10, 15) - QR(api, &workingState, 1, 6, 11, 12) - QR(api, &workingState, 2, 7, 8, 13) - QR(api, &workingState, 3, 4, 9, 14) - } - - // final step. Add initial state to working state - for i := 0; i < 16; i++ { - add32(api, &state[i], &workingState[i]) - } - -} - -func repackLSB(a *[BITS_PER_WORD]frontend.Variable) { - var res [BITS_PER_WORD]frontend.Variable - for i := 0; i < 4; i++ { - for j := 0; j < 8; j++ { - res[(3-i)*8+j] = a[i*8+j] - } - } - - for i := 0; i < BITS_PER_WORD; i++ { - a[i] = res[i] - } -} - -// Serialize repacks words in LE byte order -func Serialize(state *[16][BITS_PER_WORD]frontend.Variable) { - for i := range state { - repackLSB(&state[i]) - } -} diff --git a/gnark/circuits/toprf/testdata.go b/gnark/circuits/toprf/testdata.go index 5db721b..a04b1db 100644 --- a/gnark/circuits/toprf/testdata.go +++ b/gnark/circuits/toprf/testdata.go @@ -25,7 +25,7 @@ type TestData struct { Proof *Proof } -func PrepareTestData(secretData string, domainSeparator string) (*TOPRFParams, error) { +func PrepareTestData(secretData string, domainSeparator string) (*Params, error) { req, err := utils.OPRFGenerateRequest([]byte(secretData), domainSeparator) if err != nil { return nil, err @@ -82,7 +82,7 @@ func PrepareTestData(secretData string, domainSeparator string) (*TOPRFParams, e return nil, err } - data := &TOPRFParams{ + data := &Params{ SecretData: [2]frontend.Variable{req.SecretElements[0], req.SecretElements[1]}, DomainSeparator: new(big.Int).SetBytes([]byte(domainSeparator)), Output: out, diff --git a/gnark/circuits/toprf/toprf.go b/gnark/circuits/toprf/toprf.go index 9df3e04..4fed5b9 100644 --- a/gnark/circuits/toprf/toprf.go +++ b/gnark/circuits/toprf/toprf.go @@ -1,41 +1,77 @@ package toprf import ( + "math/big" + tbn "github.com/consensys/gnark-crypto/ecc/twistededwards" "github.com/consensys/gnark/frontend" "github.com/consensys/gnark/std/algebra/native/twistededwards" "github.com/consensys/gnark/std/hash/mimc" "github.com/consensys/gnark/std/math/bits" + "github.com/consensys/gnark/std/math/cmp" "github.com/consensys/gnark/std/math/emulated" ) const Threshold = 1 +const BytesPerElement = 31 -type TOPRFParams struct { +type Params struct { SecretData [2]frontend.Variable DomainSeparator frontend.Variable `gnark:",public"` Mask frontend.Variable - - Responses [Threshold]twistededwards.Point `gnark:",public"` // responses per each node - Coefficients [Threshold]frontend.Variable `gnark:",public"` // coeffs for reconstructing point & public key - - // Proofs of DLEQ per node + Responses [Threshold]twistededwards.Point `gnark:",public"` // responses per each node + Coefficients [Threshold]frontend.Variable `gnark:",public"` // coeffs for reconstructing point & public key SharePublicKeys [Threshold]twistededwards.Point `gnark:",public"` C [Threshold]frontend.Variable `gnark:",public"` R [Threshold]frontend.Variable `gnark:",public"` - - Output frontend.Variable `gnark:",public"` // hash of deblinded point + secret data + Output frontend.Variable `gnark:",public"` // hash of deblinded point + secret data } type TOPRF struct { - *TOPRFParams + *Params } func (n *TOPRF) Define(api frontend.API) error { - return VerifyTOPRF(api, n.TOPRFParams) + return VerifyTOPRF(api, n.Params) +} + +func ExtractSecretElements(api frontend.API, bits, bitmask []frontend.Variable, l frontend.Variable) [2]frontend.Variable { + api.AssertIsDifferent(l, 0) // Len != 0 + + totalBitsNumber := len(bits) + pow1 := frontend.Variable(1) + pow2 := frontend.Variable(0) + res1 := frontend.Variable(0) + res2 := frontend.Variable(0) + totalBits := frontend.Variable(0) + + for i := 0; i < totalBitsNumber; i++ { + bitIndex := i + bitIsSet := bitmask[bitIndex] + bit := api.Select(bitIsSet, bits[bitIndex], 0) + + res1 = api.Add(res1, api.Mul(bit, pow1)) + res2 = api.Add(res2, api.Mul(bit, pow2)) + + n := api.Add(bitIsSet, 1) // do we need to multiply power by 2? + pow1 = api.Mul(pow1, n) + pow2 = api.Mul(pow2, n) + + totalBits = api.Add(totalBits, bitIsSet) + + r1Done := api.IsZero(api.Sub(totalBits, BytesPerElement*8)) // are we done with 1st number? + pow1 = api.Mul(pow1, api.Sub(1, r1Done)) // set pow1 to zero if yes + pow2 = api.Add(pow2, r1Done) // set pow2 to 1 to start increasing + + } + + comparator := cmp.NewBoundedComparator(api, big.NewInt(int64(totalBitsNumber)), false) // max diff is number of bits + comparator.AssertIsLessEq(totalBits, BytesPerElement*8*2) // check that number of processed bits <= 62 bytes + api.AssertIsEqual(totalBits, api.Mul(l, 8)) // and that it corresponds to Len + return [2]frontend.Variable{res1, res2} } -func VerifyTOPRF(api frontend.API, p *TOPRFParams) error { +func VerifyTOPRF(api frontend.API, p *Params) error { curve, err := twistededwards.NewEdCurve(api, tbn.BN254) if err != nil { return err @@ -105,7 +141,7 @@ func toprfMul(curve twistededwards.Curve, points [Threshold]twistededwards.Point result = curve.Add(result, gki) } return result - //} + // } } func verifyDLEQ(api frontend.API, curve twistededwards.Curve, masked, response, serverPublicKey twistededwards.Point, c, r frontend.Variable) error { diff --git a/gnark/circuits/toprf/toprf_test.go b/gnark/circuits/toprf/toprf_test.go index bba47fd..4e524fa 100644 --- a/gnark/circuits/toprf/toprf_test.go +++ b/gnark/circuits/toprf/toprf_test.go @@ -18,7 +18,7 @@ func TestTOPRF(t *testing.T) { assert.NoError(err) wtns := TOPRF{ - TOPRFParams: testData, + Params: testData, } assert.CheckCircuit(&wtns, test.WithCurves(ecc.BN254), test.WithValidAssignment(&wtns)) diff --git a/gnark/keygen/keygen.go b/gnark/keygen/keygen.go index 3857f1d..d658b1c 100644 --- a/gnark/keygen/keygen.go +++ b/gnark/keygen/keygen.go @@ -8,6 +8,7 @@ import ( aes_v2_oprf "gnark-symmetric-crypto/circuits/aesV2_oprf" "gnark-symmetric-crypto/circuits/chachaV3" "gnark-symmetric-crypto/circuits/chachaV3_oprf" + "gnark-symmetric-crypto/circuits/toprf" "regexp" "time" @@ -29,11 +30,11 @@ type algCircuit struct { var algMappings = map[string]*algCircuit{ "chacha20": {"chacha20", &chachaV3.ChaChaCircuit{}}, - "aes128": {"aes-128-ctr", &aes_v2.AESWrapper{Key: make([]frontend.Variable, 16)}}, - "aes256": {"aes-256-ctr", &aes_v2.AESWrapper{Key: make([]frontend.Variable, 32)}}, - "chacha20_oprf": {"chacha20-toprf", &chachaV3_oprf.ChachaTOPRFCircuit{TOPRF: chachaV3_oprf.TOPRFData{}}}, - "aes128_oprf": {"aes-128-ctr-toprf", &aes_v2_oprf.AESWrapper{Key: make([]frontend.Variable, 16), TOPRF: aes_v2_oprf.TOPRFData{}}}, - "aes256_oprf": {"aes-256-ctr-toprf", &aes_v2_oprf.AESWrapper{Key: make([]frontend.Variable, 32), TOPRF: aes_v2_oprf.TOPRFData{}}}, + "aes128": {"aes-128-ctr", &aes_v2.AESCircuit{AESBaseCircuit: aes_v2.AESBaseCircuit{Key: make([]frontend.Variable, 16)}}}, + "aes256": {"aes-256-ctr", &aes_v2.AESCircuit{AESBaseCircuit: aes_v2.AESBaseCircuit{Key: make([]frontend.Variable, 32)}}}, + "chacha20_oprf": {"chacha20-toprf", &chachaV3_oprf.ChachaTOPRFCircuit{TOPRF: toprf.Params{}}}, + "aes128_oprf": {"aes-128-ctr-toprf", &aes_v2_oprf.AESTOPRFCircuit{AESBaseCircuit: aes_v2.AESBaseCircuit{Key: make([]frontend.Variable, 16)}, TOPRF: toprf.Params{}}}, + "aes256_oprf": {"aes-256-ctr-toprf", &aes_v2_oprf.AESTOPRFCircuit{AESBaseCircuit: aes_v2.AESBaseCircuit{Key: make([]frontend.Variable, 32)}, TOPRF: toprf.Params{}}}, } func main() { diff --git a/gnark/libraries/core_test.go b/gnark/libraries/core_test.go index 679f9ae..ef1caca 100644 --- a/gnark/libraries/core_test.go +++ b/gnark/libraries/core_test.go @@ -7,7 +7,6 @@ import ( "encoding/binary" "encoding/json" aes_v2 "gnark-symmetric-crypto/circuits/aesV2" - aes_v2_oprf "gnark-symmetric-crypto/circuits/aesV2_oprf" "gnark-symmetric-crypto/circuits/toprf" prover "gnark-symmetric-crypto/libraries/prover/impl" verifier "gnark-symmetric-crypto/libraries/verifier/impl" @@ -342,8 +341,8 @@ func TestFullAES128OPRF(t *testing.T) { assert.True(prover.InitAlgorithm(prover.AES_128_OPRF, aes128OprfKey, aes128Oprfr1cs)) bKey := make([]byte, 16) bNonce := make([]byte, 12) - bOutput := make([]byte, aes_v2_oprf.BLOCKS*16) // circuit output is plaintext - bInput := make([]byte, aes_v2_oprf.BLOCKS*16) + bOutput := make([]byte, aes_v2.BLOCKS*16) // circuit output is plaintext + bInput := make([]byte, aes_v2.BLOCKS*16) tmp, _ := rand.Int(rand.Reader, big.NewInt(math.MaxUint32)) counter := uint32(tmp.Uint64()) @@ -482,8 +481,8 @@ func TestFullAES256OPRF(t *testing.T) { assert.True(prover.InitAlgorithm(prover.AES_256_OPRF, aes256OprfKey, aes256Oprfr1cs)) bKey := make([]byte, 32) bNonce := make([]byte, 12) - bOutput := make([]byte, aes_v2_oprf.BLOCKS*16) // circuit output is plaintext - bInput := make([]byte, aes_v2_oprf.BLOCKS*16) + bOutput := make([]byte, aes_v2.BLOCKS*16) // circuit output is plaintext + bInput := make([]byte, aes_v2.BLOCKS*16) tmp, _ := rand.Int(rand.Reader, big.NewInt(math.MaxUint32)) counter := uint32(tmp.Uint64()) diff --git a/gnark/libraries/prover/impl/library.go b/gnark/libraries/prover/impl/library.go index 6654406..3bc884a 100644 --- a/gnark/libraries/prover/impl/library.go +++ b/gnark/libraries/prover/impl/library.go @@ -35,33 +35,33 @@ var algorithmNames = map[uint8]string{ var provers = map[string]*ProverParams{ "chacha20": { - KeyHash: "bf4901012e00a7517a6da2e4c4d3922d90051609726c8488b2a6045b030e44eb", - CircuitHash: "4aa80775a6721404bf8f82fd2d78d335fabbdf517762b82a7d13e6d2446c49bf", + KeyHash: "6c84adfffae0183ad7d333e324079a7787e56caa79b1d3c8894dd9cdbc942838", + CircuitHash: "4382aa593cfe8f3b3dcd35cff62a27d8ca2b415dae64bdeca2a561a707fabab0", Prover: &ChaChaProver{}, }, "aes-128-ctr": { - KeyHash: "a3c41ab381f31a0820817a8d2e928f276487da3bf3e61285791689388af27017", + KeyHash: "9cb4e54cc2a4b090ac58010b6b5c11647ecb805900f2ab5acea73b33adc68354", CircuitHash: "b1ee478f009fe81946e6e2768ef0b6d62ab266525f186baaa4e8dec61b6e3ea6", Prover: &AESProver{}, }, "aes-256-ctr": { - KeyHash: "94a9df9edad28462f1d523b191c9caf3aa07751ca5f5e4cf458614f1fc72c198", + KeyHash: "2fe18d977c42fc1696e4c4b39a26a5513918c3970db6d5bc23cf90e54348fed6", CircuitHash: "e62f8e74b17cad4012513cf23971ddf58faa63a4a87676047bccd255021dee13", Prover: &AESProver{}, }, "chacha20-toprf": { - KeyHash: "14ee5e7b36ce6b47ee4e344deafd7573a8dc9554f899f410328764a84c77f719", - CircuitHash: "b5fbd1900eabb8e3a12cf0896cfd7f5b2b6290d536e0f6bd7b9eb09caf9c0f7e", + KeyHash: "385b2bfe8c4eb2837d71a26e1610b48e1eb09ea25f1b77c42176f6d5ed981076", + CircuitHash: "a89a1dd02fa019efe7c0964e2a4e594d7843f69293a67b2bfd111da5bf998c92", Prover: &ChaChaOPRFProver{}, }, "aes-128-ctr-toprf": { - KeyHash: "a8171697bc39e84446f27652ccd6d2dfca1829e93833780068a8e0e16e44410a", - CircuitHash: "38dfce7e54a8872035c5b67e70cf74aaf6b2cc37a5ea2b7f86bea1191e7647b0", + KeyHash: "fe380af3f8a4026d214c023c3d31a14ca2e560d0361721a7b1edb97d6b9db3d4", + CircuitHash: "3d6fd6cc53bb465300785201abbb0987d1ef9ebe0400ec6107fdfa81d48c3773", Prover: &AESOPRFProver{}, }, "aes-256-ctr-toprf": { - KeyHash: "e9259ca30016f85e4c9377aee4524ebaf8af8d54e045a1d4753b04ceada68c08", - CircuitHash: "182e1cfabfdddbf91bf475544f173e74b48d40debef1e85bcf02068ffee08c97", + KeyHash: "35f6625ce05e41417dbca2fa3aa18a4f43b8973454c0a12847ade67f976fb43b", + CircuitHash: "d1e128fcfd2de96d1e296e848998d09988a52bb3bb80a9a9064175cba8e3fe69", Prover: &AESOPRFProver{}, }, } diff --git a/gnark/libraries/prover/impl/provers.go b/gnark/libraries/prover/impl/provers.go index 7ed4f59..d3a518e 100644 --- a/gnark/libraries/prover/impl/provers.go +++ b/gnark/libraries/prover/impl/provers.go @@ -27,8 +27,6 @@ func init() { // std.RegisterHints() } -const AES_BLOCKS = 4 - type TOPRFResponse struct { Index uint8 `json:"index"` PublicKeyShare []byte `json:"publicKeyShare"` @@ -166,8 +164,10 @@ func (ap *AESProver) Prove(params *InputParams) (proof []byte, output []uint8) { ctr := cipher.NewCTR(block, append(nonce, binary.BigEndian.AppendUint32(nil, counter)...)) ctr.XORKeyStream(output, input) - circuit := &aes_v2.AESWrapper{ - Key: make([]frontend.Variable, len(key)), + circuit := &aes_v2.AESCircuit{ + AESBaseCircuit: aes_v2.AESBaseCircuit{ + Key: make([]frontend.Variable, len(key)), + }, } circuit.Counter = counter @@ -220,8 +220,8 @@ func (cp *ChaChaOPRFProver) Prove(params *InputParams) (proof []byte, output []u if len(nonce) != 12 { log.Panicf("nonce length must be 12: %d", len(nonce)) } - if len(input) != chachaV3_oprf.Blocks*64 { - log.Panicf("input length must be %d: %d", chachaV3_oprf.Blocks*64, len(input)) + if len(input) != chachaV3.Blocks*64 { + log.Panicf("input length must be %d: %d", chachaV3.Blocks*64, len(input)) } // calculate ciphertext ourselves @@ -267,15 +267,16 @@ func (cp *ChaChaOPRFProver) Prove(params *InputParams) (proof []byte, output []u } witness := &chachaV3_oprf.ChachaTOPRFCircuit{ - TOPRF: chachaV3_oprf.TOPRFData{ - DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), - Mask: new(big.Int).SetBytes(oprf.Mask), - Output: new(big.Int).SetBytes(oprf.Output), - EvaluatedElements: resps, - Coefficients: coeffs, - PublicKeys: pubKeys, - C: cs, - R: rs, + TOPRF: toprf.Params{ + SecretData: [2]frontend.Variable{0, 0}, // will be rewritten + DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), + Mask: new(big.Int).SetBytes(oprf.Mask), + Output: new(big.Int).SetBytes(oprf.Output), + Responses: resps, + Coefficients: coeffs, + SharePublicKeys: pubKeys, + C: cs, + R: rs, }, } @@ -323,8 +324,8 @@ func (ap *AESOPRFProver) Prove(params *InputParams) (proof []byte, output []uint if len(nonce) != 12 { log.Panicf("nonce length must be 12: %d", len(nonce)) } - if len(input) != aes_v2_oprf.BLOCKS*16 { - log.Panicf("input length must be %d: %d", aes_v2_oprf.BLOCKS*16, len(input)) + if len(input) != aes_v2.BLOCKS*16 { + log.Panicf("input length must be %d: %d", aes_v2.BLOCKS*16, len(input)) } block, err := aes.NewCipher(key) @@ -356,17 +357,18 @@ func (ap *AESOPRFProver) Prove(params *InputParams) (proof []byte, output []uint coeffs[i] = utils.Coeff(idxs[i], idxs) } - circuit := &aes_v2_oprf.AESWrapper{ - Key: make([]frontend.Variable, len(key)), - TOPRF: aes_v2_oprf.TOPRFData{ - DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), - Mask: new(big.Int).SetBytes(oprf.Mask), - Output: new(big.Int).SetBytes(oprf.Output), - EvaluatedElements: resps, - Coefficients: coeffs, - PublicKeys: pubKeys, - C: cs, - R: rs, + circuit := &aes_v2_oprf.AESTOPRFCircuit{ + AESBaseCircuit: aes_v2.AESBaseCircuit{Key: make([]frontend.Variable, len(key))}, + TOPRF: toprf.Params{ + SecretData: [2]frontend.Variable{0, 0}, // will be rewritten + DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), + Mask: new(big.Int).SetBytes(oprf.Mask), + Output: new(big.Int).SetBytes(oprf.Output), + Responses: resps, + Coefficients: coeffs, + SharePublicKeys: pubKeys, + C: cs, + R: rs, }, } diff --git a/gnark/libraries/verifier/impl/generated/vk.aes128 b/gnark/libraries/verifier/impl/generated/vk.aes128 index 0d442d3..e1dfe06 100755 Binary files a/gnark/libraries/verifier/impl/generated/vk.aes128 and b/gnark/libraries/verifier/impl/generated/vk.aes128 differ diff --git a/gnark/libraries/verifier/impl/generated/vk.aes128_oprf b/gnark/libraries/verifier/impl/generated/vk.aes128_oprf index 56e3838..13da312 100755 Binary files a/gnark/libraries/verifier/impl/generated/vk.aes128_oprf and b/gnark/libraries/verifier/impl/generated/vk.aes128_oprf differ diff --git a/gnark/libraries/verifier/impl/generated/vk.aes256 b/gnark/libraries/verifier/impl/generated/vk.aes256 index 1679b35..2fe300f 100755 Binary files a/gnark/libraries/verifier/impl/generated/vk.aes256 and b/gnark/libraries/verifier/impl/generated/vk.aes256 differ diff --git a/gnark/libraries/verifier/impl/generated/vk.aes256_oprf b/gnark/libraries/verifier/impl/generated/vk.aes256_oprf index 696d816..6da3f2f 100755 Binary files a/gnark/libraries/verifier/impl/generated/vk.aes256_oprf and b/gnark/libraries/verifier/impl/generated/vk.aes256_oprf differ diff --git a/gnark/libraries/verifier/impl/generated/vk.chacha20 b/gnark/libraries/verifier/impl/generated/vk.chacha20 index 52f203a..b1de10c 100755 Binary files a/gnark/libraries/verifier/impl/generated/vk.chacha20 and b/gnark/libraries/verifier/impl/generated/vk.chacha20 differ diff --git a/gnark/libraries/verifier/impl/generated/vk.chacha20_oprf b/gnark/libraries/verifier/impl/generated/vk.chacha20_oprf index e39fd4a..261e4bd 100755 Binary files a/gnark/libraries/verifier/impl/generated/vk.chacha20_oprf and b/gnark/libraries/verifier/impl/generated/vk.chacha20_oprf differ diff --git a/gnark/libraries/verifier/impl/verifiers.go b/gnark/libraries/verifier/impl/verifiers.go index 0252544..349542b 100644 --- a/gnark/libraries/verifier/impl/verifiers.go +++ b/gnark/libraries/verifier/impl/verifiers.go @@ -88,8 +88,8 @@ func (av *AESVerifier) Verify(bProof []byte, publicSignals []uint8) bool { nonce := publicSignals[bytesPerInput : bytesPerInput+12] bCounter := publicSignals[bytesPerInput+12 : bytesPerInput+12+4] - witness := &aes_v2.AESWrapper{ - Key: make([]frontend.Variable, 1), // avoid warnings + witness := &aes_v2.AESCircuit{ + AESBaseCircuit: aes_v2.AESBaseCircuit{Key: make([]frontend.Variable, 1)}, // avoid warnings } for i := 0; i < len(plaintext); i++ { @@ -169,14 +169,14 @@ func (cv *ChachaOPRFVerifier) Verify(proof []byte, publicSignals []uint8) bool { } witness := &chachaV3_oprf.ChachaTOPRFCircuit{ - TOPRF: chachaV3_oprf.TOPRFData{ - DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), - EvaluatedElements: evals, - Coefficients: coeffs, - PublicKeys: nodePublicKeys, - C: cs, - R: rs, - Output: new(big.Int).SetBytes(oprf.Output), + TOPRF: toprf.Params{ + DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), + Responses: evals, + Coefficients: coeffs, + SharePublicKeys: nodePublicKeys, + C: cs, + R: rs, + Output: new(big.Int).SetBytes(oprf.Output), }, } @@ -253,16 +253,16 @@ func (cv *AESOPRFVerifier) Verify(proof []byte, publicSignals []uint8) bool { coeffs[i] = utils.Coeff(idxs[i], idxs) } - witness := &aes_v2_oprf.AESWrapper{ - Key: make([]frontend.Variable, 1), // to avoid warnings - TOPRF: aes_v2_oprf.TOPRFData{ - DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), - EvaluatedElements: evals, - Coefficients: coeffs, - PublicKeys: nodePublicKeys, - C: cs, - R: rs, - Output: new(big.Int).SetBytes(oprf.Output), + witness := &aes_v2_oprf.AESTOPRFCircuit{ + AESBaseCircuit: aes_v2.AESBaseCircuit{Key: make([]frontend.Variable, 1)}, // avoid warnings + TOPRF: toprf.Params{ + DomainSeparator: new(big.Int).SetBytes(oprf.DomainSeparator), + Responses: evals, + Coefficients: coeffs, + SharePublicKeys: nodePublicKeys, + C: cs, + R: rs, + Output: new(big.Int).SetBytes(oprf.Output), }, } diff --git a/resources/gnark/pk.aes128 b/resources/gnark/pk.aes128 index 0a2c8d6..3ab68af 100755 Binary files a/resources/gnark/pk.aes128 and b/resources/gnark/pk.aes128 differ diff --git a/resources/gnark/pk.aes128_oprf b/resources/gnark/pk.aes128_oprf index 1352e49..c39cfc5 100755 Binary files a/resources/gnark/pk.aes128_oprf and b/resources/gnark/pk.aes128_oprf differ diff --git a/resources/gnark/pk.aes256 b/resources/gnark/pk.aes256 index e6c0fd0..3697ab1 100755 Binary files a/resources/gnark/pk.aes256 and b/resources/gnark/pk.aes256 differ diff --git a/resources/gnark/pk.aes256_oprf b/resources/gnark/pk.aes256_oprf index e65a545..93fcb83 100755 Binary files a/resources/gnark/pk.aes256_oprf and b/resources/gnark/pk.aes256_oprf differ diff --git a/resources/gnark/pk.chacha20 b/resources/gnark/pk.chacha20 index 233f12d..9c31cbe 100755 Binary files a/resources/gnark/pk.chacha20 and b/resources/gnark/pk.chacha20 differ diff --git a/resources/gnark/pk.chacha20_oprf b/resources/gnark/pk.chacha20_oprf index 379d2bc..4c43c57 100755 Binary files a/resources/gnark/pk.chacha20_oprf and b/resources/gnark/pk.chacha20_oprf differ diff --git a/resources/gnark/r1cs.aes128_oprf b/resources/gnark/r1cs.aes128_oprf index 99d688b..6b1e262 100755 Binary files a/resources/gnark/r1cs.aes128_oprf and b/resources/gnark/r1cs.aes128_oprf differ diff --git a/resources/gnark/r1cs.aes256_oprf b/resources/gnark/r1cs.aes256_oprf index 93aad67..b612d4a 100755 Binary files a/resources/gnark/r1cs.aes256_oprf and b/resources/gnark/r1cs.aes256_oprf differ diff --git a/resources/gnark/r1cs.chacha20 b/resources/gnark/r1cs.chacha20 index a1c8b8d..9a6b710 100755 Binary files a/resources/gnark/r1cs.chacha20 and b/resources/gnark/r1cs.chacha20 differ diff --git a/resources/gnark/r1cs.chacha20_oprf b/resources/gnark/r1cs.chacha20_oprf index 4e8d16c..8428ec7 100755 Binary files a/resources/gnark/r1cs.chacha20_oprf and b/resources/gnark/r1cs.chacha20_oprf differ