diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml index 490c8e4..22a4bd0 100644 --- a/.github/workflows/e2e.yaml +++ b/.github/workflows/e2e.yaml @@ -41,9 +41,18 @@ jobs: run: | npm install -g snarkjs - - name: Checkout circuits + - name: Checkout Zeto uses: actions/checkout@v4 with: + path: zeto + fetch-depth: 0 + + - name: Checkout kaleido's fork of go-iden3-crypto + uses: actions/checkout@v3 + with: + path: go-iden3-crypto + repository: kaleido-io/go-iden3-crypto + ref: multi-states fetch-depth: 0 - name: Setup temp dir for the artifacts @@ -55,7 +64,7 @@ jobs: CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts PTAU_DOWNLOAD_PATH: ${{ runner.temp }}/zeto-artifacts - working-directory: zkp/circuits + working-directory: zeto/zkp/circuits run: | npm install npm run gen @@ -64,7 +73,7 @@ jobs: env: PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts - working-directory: go-sdk + working-directory: zeto/go-sdk run: | make e2e @@ -72,7 +81,7 @@ jobs: env: PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts - working-directory: zkp/js + working-directory: zeto/zkp/js run: | npm install npm run test:e2e @@ -81,7 +90,7 @@ jobs: env: PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts - working-directory: solidity + working-directory: zeto/solidity run: | npm install npm t @@ -91,7 +100,7 @@ jobs: USE_FACTORY: true PROVING_KEYS_ROOT: ${{ runner.temp }}/zeto-artifacts CIRCUITS_ROOT: ${{ runner.temp }}/zeto-artifacts - working-directory: solidity + working-directory: zeto/solidity run: | npm install npm t diff --git a/.github/workflows/golang.yaml b/.github/workflows/golang.yaml index 2009237..38f505b 100644 --- a/.github/workflows/golang.yaml +++ b/.github/workflows/golang.yaml @@ -28,5 +28,5 @@ jobs: - uses: codecov/codecov-action@v4 with: - codecov_yml_path: ./codecov.yml + codecov_yml_path: ./go-sdk/coverage.txt token: ${{ secrets.CODECOV_TOKEN }} diff --git a/go-sdk/go.mod b/go-sdk/go.mod index 906fe30..1a3a66e 100644 --- a/go-sdk/go.mod +++ b/go-sdk/go.mod @@ -32,8 +32,8 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/x-cray/logrus-prefixed-formatter v0.5.2 // indirect golang.org/x/sync v0.8.0 // indirect - golang.org/x/term v0.23.0 // indirect - golang.org/x/text v0.17.0 // indirect + golang.org/x/term v0.24.0 // indirect + golang.org/x/text v0.18.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect ) @@ -46,10 +46,12 @@ require ( github.com/iden3/go-rapidsnark/types v0.0.2 // indirect github.com/iden3/go-rapidsnark/witness/v2 v2.0.0 github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - golang.org/x/crypto v0.26.0 // indirect - golang.org/x/sys v0.24.0 // indirect + golang.org/x/crypto v0.27.0 // indirect + golang.org/x/sys v0.25.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect gorm.io/driver/postgres v1.5.9 gorm.io/driver/sqlite v1.5.6 gorm.io/gorm v1.25.11 ) + +replace github.com/iden3/go-iden3-crypto => github.com/kaleido-io/go-iden3-crypto v0.0.0-20240905154504-333f60687c26 diff --git a/go-sdk/go.sum b/go-sdk/go.sum index 26d8360..c2cc79e 100644 --- a/go-sdk/go.sum +++ b/go-sdk/go.sum @@ -25,8 +25,6 @@ github.com/hyperledger/firefly-common v1.4.6 h1:qqXoSaRml3WjUnWcWxrrXs5AIOWa+UcM github.com/hyperledger/firefly-common v1.4.6/go.mod h1:jkErZdQmC9fsAJZQO427tURdwB9iiW+NMUZSqS3eBIE= github.com/hyperledger/firefly-signer v1.1.13 h1:eiHjc6HPRG8AzXUCUgm51qqX1I9BokiuiiqJ89XwK4M= github.com/hyperledger/firefly-signer v1.1.13/go.mod h1:pK6kivzBFSue3zpJSQpH67VasnLLbwBJOBUNv0zHbRA= -github.com/iden3/go-iden3-crypto v0.0.16 h1:zN867xiz6HgErXVIV/6WyteGcOukE9gybYTorBMEdsk= -github.com/iden3/go-iden3-crypto v0.0.16/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= github.com/iden3/go-rapidsnark/prover v0.0.10 h1:NvOfRPpex/k646UsqOcUy7a7uVl17t4ok9kWvpQg4+k= github.com/iden3/go-rapidsnark/prover v0.0.10/go.mod h1:wgDsmKOGCuWGtgVtuW9ARWNguNr4NJAIyg2G7+uTax0= github.com/iden3/go-rapidsnark/types v0.0.2 h1:CjJSrlbWchHzuMRdxSYrEh7n/akP+Z2PLNbwT5yBmQY= @@ -49,6 +47,8 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kaleido-io/go-iden3-crypto v0.0.0-20240905154504-333f60687c26 h1:WHlF6wdeN4sJ9iB38fLpIm0tugEwmBzoxgWIHXXd1qU= +github.com/kaleido-io/go-iden3-crypto v0.0.0-20240905154504-333f60687c26/go.mod h1:dLpM4vEPJ3nDHzhWFXDjzkn1qHoBeOT/3UEhXsEsP3E= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= @@ -88,8 +88,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg= github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE= -golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw= -golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54= +golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= +golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ= @@ -97,12 +97,12 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg= -golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU= -golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk= -golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc= -golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= +golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM= +golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8= +golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224= +golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= diff --git a/go-sdk/integration-test/db_test.go b/go-sdk/integration-test/db_test.go index dfb4a9f..93b29b9 100644 --- a/go-sdk/integration-test/db_test.go +++ b/go-sdk/integration-test/db_test.go @@ -22,11 +22,11 @@ import ( "testing" "github.com/hyperledger-labs/zeto/go-sdk/internal/testutils" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/crypto" "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/smt" "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/storage" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/utxo" "github.com/stretchr/testify/assert" "gorm.io/driver/postgres" "gorm.io/driver/sqlite" @@ -68,7 +68,7 @@ func TestSqliteStorage(t *testing.T) { uriString := "https://example.com/token/1001" assert.NoError(t, err) sender := testutils.NewKeypair() - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() utxo1 := node.NewNonFungible(tokenId, uriString, sender.PublicKey, salt1) n1, err := node.NewLeafNode(utxo1) @@ -116,7 +116,7 @@ func TestPostgresStorage(t *testing.T) { tokenUri := "https://example.com/token/1001" assert.NoError(t, err) sender := testutils.NewKeypair() - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() utxo1 := node.NewNonFungible(tokenId, tokenUri, sender.PublicKey, salt1) n1, err := node.NewLeafNode(utxo1) diff --git a/go-sdk/integration-test/e2e_test.go b/go-sdk/integration-test/e2e_test.go index 8773f17..bfd25a9 100644 --- a/go-sdk/integration-test/e2e_test.go +++ b/go-sdk/integration-test/e2e_test.go @@ -26,6 +26,7 @@ import ( "time" "github.com/hyperledger-labs/zeto/go-sdk/internal/testutils" + "github.com/hyperledger-labs/zeto/go-sdk/pkg/crypto" keyscore "github.com/hyperledger-labs/zeto/go-sdk/pkg/key-manager/core" "github.com/hyperledger-labs/zeto/go-sdk/pkg/key-manager/key" "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/node" @@ -124,15 +125,15 @@ func TestZeto_1_SuccessfulProving(t *testing.T) { inputValues := []*big.Int{big.NewInt(30), big.NewInt(40)} outputValues := []*big.Int{big.NewInt(32), big.NewInt(38)} - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() input1, _ := poseidon.Hash([]*big.Int{inputValues[0], salt1, sender.PublicKey.X, sender.PublicKey.Y}) - salt2 := utxo.NewSalt() + salt2 := crypto.NewSalt() input2, _ := poseidon.Hash([]*big.Int{inputValues[1], salt2, sender.PublicKey.X, sender.PublicKey.Y}) inputCommitments := []*big.Int{input1, input2} - salt3 := utxo.NewSalt() + salt3 := crypto.NewSalt() output1, _ := poseidon.Hash([]*big.Int{outputValues[0], salt3, receiver.PublicKey.X, receiver.PublicKey.Y}) - salt4 := utxo.NewSalt() + salt4 := crypto.NewSalt() output2, _ := poseidon.Hash([]*big.Int{outputValues[1], salt4, sender.PublicKey.X, sender.PublicKey.Y}) outputCommitments := []*big.Int{output1, output2} @@ -185,19 +186,19 @@ func TestZeto_2_SuccessfulProving(t *testing.T) { inputValues := []*big.Int{big.NewInt(30), big.NewInt(40)} outputValues := []*big.Int{big.NewInt(32), big.NewInt(38)} - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() input1, _ := poseidon.Hash([]*big.Int{inputValues[0], salt1, sender.PublicKey.X, sender.PublicKey.Y}) - salt2 := utxo.NewSalt() + salt2 := crypto.NewSalt() input2, _ := poseidon.Hash([]*big.Int{inputValues[1], salt2, sender.PublicKey.X, sender.PublicKey.Y}) inputCommitments := []*big.Int{input1, input2} - salt3 := utxo.NewSalt() + salt3 := crypto.NewSalt() output1, _ := poseidon.Hash([]*big.Int{outputValues[0], salt3, receiver.PublicKey.X, receiver.PublicKey.Y}) - salt4 := utxo.NewSalt() + salt4 := crypto.NewSalt() output2, _ := poseidon.Hash([]*big.Int{outputValues[1], salt4, sender.PublicKey.X, sender.PublicKey.Y}) outputCommitments := []*big.Int{output1, output2} - encryptionNonce := utxo.NewEncryptionNonce() + encryptionNonce := crypto.NewEncryptionNonce() witnessInputs := map[string]interface{}{ "inputCommitments": inputCommitments, @@ -224,6 +225,30 @@ func TestZeto_2_SuccessfulProving(t *testing.T) { assert.Equal(t, 3, len(proof.Proof.B)) assert.Equal(t, 3, len(proof.Proof.C)) assert.Equal(t, 9, len(proof.PubSignals)) + + // the receiver would be able to get the encrypted values and salts + // from the transaction events + encryptedValues := make([]*big.Int, 4) + for i := 0; i < 4; i++ { + v, ok := new(big.Int).SetString(proof.PubSignals[i], 10) + assert.True(t, ok) + encryptedValues[i] = v + } + + // the first two elements in the public signals are the encrypted value and salt + // for the first output. decrypt using the receiver's private key and compare with + // the UTXO hash + secret := crypto.GenerateECDHSharedSecret(receiver.PrivateKey, sender.PublicKey) + decrypted, err := crypto.PoseidonDecrypt(encryptedValues, []*big.Int{secret.X, secret.Y}, encryptionNonce, 2) + assert.NoError(t, err) + assert.Equal(t, outputValues[0].String(), decrypted[0].String()) + assert.Equal(t, salt3.String(), decrypted[1].String()) + + // as the receiver, to check if the decryption was successful, we hash the decrypted + // value and salt and compare with the output commitment + calculatedHash, err := poseidon.Hash([]*big.Int{decrypted[0], decrypted[1], receiver.PublicKey.X, receiver.PublicKey.Y}) + assert.NoError(t, err) + assert.Equal(t, output1.String(), calculatedHash.String()) } func TestZeto_3_SuccessfulProving(t *testing.T) { @@ -237,9 +262,9 @@ func TestZeto_3_SuccessfulProving(t *testing.T) { inputValues := []*big.Int{big.NewInt(30), big.NewInt(40)} outputValues := []*big.Int{big.NewInt(32), big.NewInt(38)} - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() input1, _ := poseidon.Hash([]*big.Int{inputValues[0], salt1, sender.PublicKey.X, sender.PublicKey.Y}) - salt2 := utxo.NewSalt() + salt2 := crypto.NewSalt() input2, _ := poseidon.Hash([]*big.Int{inputValues[1], salt2, sender.PublicKey.X, sender.PublicKey.Y}) inputCommitments := []*big.Int{input1, input2} @@ -268,9 +293,9 @@ func TestZeto_3_SuccessfulProving(t *testing.T) { circomProof2, err := proof2.ToCircomVerifierProof(input2, input2, mt.Root(), MAX_HEIGHT) assert.NoError(t, err) - salt3 := utxo.NewSalt() + salt3 := crypto.NewSalt() output1, _ := poseidon.Hash([]*big.Int{outputValues[0], salt3, receiver.PublicKey.X, receiver.PublicKey.Y}) - salt4 := utxo.NewSalt() + salt4 := crypto.NewSalt() output2, _ := poseidon.Hash([]*big.Int{outputValues[1], salt4, sender.PublicKey.X, sender.PublicKey.Y}) outputCommitments := []*big.Int{output1, output2} @@ -323,9 +348,9 @@ func TestZeto_4_SuccessfulProving(t *testing.T) { inputValues := []*big.Int{big.NewInt(30), big.NewInt(40)} outputValues := []*big.Int{big.NewInt(32), big.NewInt(38)} - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() input1, _ := poseidon.Hash([]*big.Int{inputValues[0], salt1, sender.PublicKey.X, sender.PublicKey.Y}) - salt2 := utxo.NewSalt() + salt2 := crypto.NewSalt() input2, _ := poseidon.Hash([]*big.Int{inputValues[1], salt2, sender.PublicKey.X, sender.PublicKey.Y}) inputCommitments := []*big.Int{input1, input2} @@ -354,13 +379,13 @@ func TestZeto_4_SuccessfulProving(t *testing.T) { circomProof2, err := proof2.ToCircomVerifierProof(input2, input2, mt.Root(), MAX_HEIGHT) assert.NoError(t, err) - salt3 := utxo.NewSalt() + salt3 := crypto.NewSalt() output1, _ := poseidon.Hash([]*big.Int{outputValues[0], salt3, receiver.PublicKey.X, receiver.PublicKey.Y}) - salt4 := utxo.NewSalt() + salt4 := crypto.NewSalt() output2, _ := poseidon.Hash([]*big.Int{outputValues[1], salt4, sender.PublicKey.X, sender.PublicKey.Y}) outputCommitments := []*big.Int{output1, output2} - encryptionNonce := utxo.NewEncryptionNonce() + encryptionNonce := crypto.NewEncryptionNonce() proof1Siblings := make([]*big.Int, len(circomProof1.Siblings)-1) for i, s := range circomProof1.Siblings[0 : len(circomProof1.Siblings)-1] { @@ -413,11 +438,11 @@ func TestZeto_5_SuccessfulProving(t *testing.T) { tokenUri, err := utxo.HashTokenUri("https://example.com/token/1001") assert.NoError(t, err) - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() input1, err := poseidon.Hash([]*big.Int{tokenId, tokenUri, salt1, sender.PublicKey.X, sender.PublicKey.Y}) assert.NoError(t, err) - salt3 := utxo.NewSalt() + salt3 := crypto.NewSalt() output1, err := poseidon.Hash([]*big.Int{tokenId, tokenUri, salt3, receiver.PublicKey.X, receiver.PublicKey.Y}) assert.NoError(t, err) @@ -472,7 +497,7 @@ func TestZeto_6_SuccessfulProving(t *testing.T) { tokenUri, err := utxo.HashTokenUri(uriString) assert.NoError(t, err) - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() input1, err := poseidon.Hash([]*big.Int{tokenId, tokenUri, salt1, sender.PublicKey.X, sender.PublicKey.Y}) assert.NoError(t, err) @@ -494,7 +519,7 @@ func TestZeto_6_SuccessfulProving(t *testing.T) { proof1Siblings[i] = s.BigInt() } - salt3 := utxo.NewSalt() + salt3 := crypto.NewSalt() output1, err := poseidon.Hash([]*big.Int{tokenId, tokenUri, salt3, receiver.PublicKey.X, receiver.PublicKey.Y}) assert.NoError(t, err) diff --git a/go-sdk/internal/crypto/ecdh.go b/go-sdk/internal/crypto/ecdh.go new file mode 100644 index 0000000..2b5e5a2 --- /dev/null +++ b/go-sdk/internal/crypto/ecdh.go @@ -0,0 +1,24 @@ +// Copyright © 2024 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package crypto + +import "github.com/iden3/go-iden3-crypto/babyjub" + +func GenerateECDHSharedSecret(privKey *babyjub.PrivateKey, pubKey *babyjub.PublicKey) *babyjub.Point { + privKeyForZkp := babyjub.SkToBigInt(privKey) + return babyjub.NewPoint().Mul(privKeyForZkp, pubKey.Point()) +} diff --git a/go-sdk/internal/crypto/ecdh_test.go b/go-sdk/internal/crypto/ecdh_test.go new file mode 100644 index 0000000..fd00238 --- /dev/null +++ b/go-sdk/internal/crypto/ecdh_test.go @@ -0,0 +1,47 @@ +// Copyright © 2024 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package crypto + +import ( + "encoding/hex" + "math/big" + "testing" + + "github.com/hyperledger-labs/zeto/go-sdk/internal/key-manager/key" + "github.com/iden3/go-iden3-crypto/babyjub" + "github.com/stretchr/testify/assert" +) + +func TestGenerateECDHSharedSecret(t *testing.T) { + privKeyBytes, _ := hex.DecodeString("071ee2e78079dc65acedf508c6b7925fc9da9256b1fa587904f023dbd90f14f6") + keyEntry := key.NewKeyEntryFromPrivateKeyBytes([32]byte(privKeyBytes)) + assert.Equal(t, "4532338485190197018098951877626853959968121575106182738140659704351327237813", babyjub.SkToBigInt(keyEntry.PrivateKey).Text(10)) + + pubKeyBytes := []byte("3f7d4633f5e4ae60d005480ca8f8cfb3fd2fcd27c33e5354743831eb2454de82") + var otherPartyPublicKey babyjub.PublicKey + err := otherPartyPublicKey.UnmarshalText(pubKeyBytes) + assert.NoError(t, err) + + sharedSecret := GenerateECDHSharedSecret(keyEntry.PrivateKey, &otherPartyPublicKey) + x, _ := new(big.Int).SetString("541910283019899847970697361288826370780990225479189552333070747388630510763", 10) + y, _ := new(big.Int).SetString("137416317897045242441765515781734290757020753544555776335583609907821726489", 10) + expected := &babyjub.Point{ + X: x, + Y: y, + } + assert.Equal(t, expected, sharedSecret) +} diff --git a/go-sdk/internal/crypto/encryption.go b/go-sdk/internal/crypto/encryption.go new file mode 100644 index 0000000..4a07bfe --- /dev/null +++ b/go-sdk/internal/crypto/encryption.go @@ -0,0 +1,178 @@ +// Copyright © 2024 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package crypto + +import ( + "fmt" + "math/big" + + "github.com/iden3/go-iden3-crypto/ff" + "github.com/iden3/go-iden3-crypto/poseidon" +) + +var two128 big.Int + +func init() { + two128.SetString(TWO_POW_128, 10) +} + +// Implements the encryption and decryption functions using Poseidon hash +// as described: https://drive.google.com/file/d/1EVrP3DzoGbmzkRmYnyEDcIQcXVU7GlOd/view +// The encryption and decryption functions are compatible with the circom implementations, +// meaning the cipher texts encrypted by the circuit in circuits/lib/encrypt.circom can +// be decrypted by the PoseidonDecrypt function. And vice versa. +func PoseidonEncrypt(msg []*big.Int, key []*big.Int, nonce *big.Int) ([]*big.Int, error) { + if len(key) != 2 { + return nil, fmt.Errorf("the key must have 2 elements, but got %d", len(key)) + } + if nonce.Cmp(&two128) >= 0 { + return nil, fmt.Errorf("the nonce must be less than 2^128, but got %s", nonce.String()) + } + + // the size of the message array must be a multiple of 3 + // pad with 0 if necessary + length := len(msg) + if length%3 > 0 { + length += 3 - (length % 3) + } + message := make([]*big.Int, length) + copy(message, msg) + for idx := len(msg); idx < length; idx++ { + message[idx] = big.NewInt(0) + } + + // Create the initial state + // S = (0, kS[0], kS[1], N + l * 2^128) + l := big.NewInt(int64(len(msg))) + state := []*big.Int{big.NewInt(0), key[0], key[1], new(big.Int).Add(nonce, new(big.Int).Mul(l, &two128))} + + cipherText := make([]*big.Int, length+1) + var err error + + n := length / 3 + i := 0 + for ; i < n; i++ { + // Iterate Poseidon on the state + state, err = poseidon.HashEx(state, 4) + if err != nil { + return nil, err + } + + // Modify the state for the next round + // state[1] = addMod(message[i * 3], state[1]); + state[1] = ff.NewElement().Add(ff.NewElement().SetBigInt(message[i*3]), ff.NewElement().SetBigInt(state[1])).ToBigIntRegular(state[1]) + // state[2] = addMod(message[i * 3 + 1], state[2]); + state[2] = ff.NewElement().Add(ff.NewElement().SetBigInt(message[i*3+1]), ff.NewElement().SetBigInt(state[2])).ToBigIntRegular(state[2]) + // state[3] = addMod(message[i * 3 + 2], state[3]); + state[3] = ff.NewElement().Add(ff.NewElement().SetBigInt(message[i*3+2]), ff.NewElement().SetBigInt(state[3])).ToBigIntRegular(state[3]) + + // Record the three elements of the encrypted message + cipherText[i*3] = state[1] + cipherText[i*3+1] = state[2] + cipherText[i*3+2] = state[3] + } + + // Iterate Poseidon on the state one last time + state, err = poseidon.HashEx(state, 4) + if err != nil { + return nil, err + } + + // Record the last ciphertext element + cipherText[i*3] = state[1] + + return cipherText, nil +} + +// the "cipherText" parameter must be the output of the PoseidonEncrypt function, with potentially +// extra elements due to the padding of the plain text messages with 0s to make the length to be 3n. +// +// the "length" parameter must be the length of the original message +func PoseidonDecrypt(cipherText []*big.Int, key []*big.Int, nonce *big.Int, length int) ([]*big.Int, error) { + if len(key) != 2 { + return nil, fmt.Errorf("the key must have 2 elements, but got %d", len(key)) + } + // length of the cipher text must be 3n+1 + if len(cipherText)%3 != 1 { + return nil, fmt.Errorf("the length of the cipher text must be 3n+1, but got %d", len(cipherText)) + } + if nonce.Cmp(&two128) >= 0 { + return nil, fmt.Errorf("the nonce must be less than 2^128, but got %s", nonce.String()) + } + if length < 1 || length > len(cipherText)-1 { + return nil, fmt.Errorf("the length must be between 1 and %d (length of cipher text array - 1), but got %d", len(cipherText)-1, length) + } + + // Create the initial state + // S = (0, kS[0], kS[1], N + l * 2^128) + l := big.NewInt(int64(length)) + state := []*big.Int{big.NewInt(0), key[0], key[1], new(big.Int).Add(nonce, new(big.Int).Mul(l, &two128))} + + message := make([]*big.Int, len(cipherText)-1) + + // Decrypt the message + n := len(cipherText) - 1 + var err error + i := 0 + for ; i < n/3; i++ { + // Iterate Poseidon on the state + state, err = poseidon.HashEx(state, 4) + if err != nil { + return nil, err + } + + // Modify the state for the next round + // message[1] = subMod(cipherText[i * 3], state[1]); + message[i*3] = ff.NewElement().Sub(ff.NewElement().SetBigInt(cipherText[i*3]), ff.NewElement().SetBigInt(state[1])).ToBigIntRegular(state[1]) + // state[2] = subMod(cipherText[i * 3 + 1], state[2]); + message[i*3+1] = ff.NewElement().Sub(ff.NewElement().SetBigInt(cipherText[i*3+1]), ff.NewElement().SetBigInt(state[2])).ToBigIntRegular(state[2]) + // state[3] = subMod(cipherText[i * 3 + 2], state[3]); + message[i*3+2] = ff.NewElement().Sub(ff.NewElement().SetBigInt(cipherText[i*3+2]), ff.NewElement().SetBigInt(state[3])).ToBigIntRegular(state[3]) + + state[1] = cipherText[i*3] + state[2] = cipherText[i*3+1] + state[3] = cipherText[i*3+2] + } + + // If length > 3, check if the last (3 - (l mod 3)) elements of the message are 0 + // this is a safty check because the message would have been padded with 0s + if length > 3 { + if length%3 == 2 { + if message[len(message)-1].Cmp(big.NewInt(0)) != 0 { + return nil, fmt.Errorf("the last element of the decrypted text must be 0") + } + } else if length%3 == 1 { + if message[len(message)-2].Cmp(big.NewInt(0)) != 0 || message[len(message)-1].Cmp(big.NewInt(0)) != 0 { + return nil, fmt.Errorf("the last two elements of the decrypted text must be 0") + } + } + } + + // Iterate Poseidon on the state one last time + state, err = poseidon.HashEx(state, 4) + if err != nil { + return nil, err + } + + // another satety check. The last element of the cipher text + // must match the 2nd element of the state from the last round + if state[1].Cmp(cipherText[len(cipherText)-1]) != 0 { + return nil, fmt.Errorf("the last element of the cipher text does not match the 2nd element of the state from the last round") + } + + return message[:length], nil +} diff --git a/go-sdk/internal/crypto/encryption_test.go b/go-sdk/internal/crypto/encryption_test.go new file mode 100644 index 0000000..cd952de --- /dev/null +++ b/go-sdk/internal/crypto/encryption_test.go @@ -0,0 +1,255 @@ +// Copyright © 2024 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package crypto + +import ( + "math/big" + "testing" + + "github.com/iden3/go-iden3-crypto/poseidon" + "github.com/stretchr/testify/assert" +) + +func TestPoseidonEx(t *testing.T) { + inputs := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901), big.NewInt(3456789012), big.NewInt(4567890123)} + result, err := poseidon.HashEx(inputs, 4) + assert.NoError(t, err) + v1, _ := new(big.Int).SetString("11501175702185290676838936720607163128640649954538569247536215022783738807825", 10) + v2, _ := new(big.Int).SetString("6120530503331008674221484812464542651389744817446661784148353548752694803697", 10) + v3, _ := new(big.Int).SetString("7124537720832890754896732787915836308362735099852420819626168545145412807782", 10) + v4, _ := new(big.Int).SetString("1847024135263482697175502194253586321631751560455204108489523026877035496452", 10) + assert.Equal(t, result, []*big.Int{v1, v2, v3, v4}) +} + +func TestPoseidonEncrypt(t *testing.T) { + x, _ := new(big.Int).SetString("2225468530552752510522780019536893048169408270351832766087923920964657502364", 10) + y, _ := new(big.Int).SetString("18264896395019517559018400396898398442219687903646821466907778802937824776999", 10) + key := []*big.Int{x, y} + nonce, _ := new(big.Int).SetString("220373351579243596212522709113509916796", 10) + msg := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901)} + cipherText, err := PoseidonEncrypt(msg, key, nonce) + assert.NoError(t, err) + assert.Equal(t, "3623473636383738070031324804097049685564466189577273141109672613904615191520", cipherText[0].Text(10)) + assert.Equal(t, "14192366070411288656840625597415252300006763456323771445497376523077328650161", cipherText[1].Text(10)) + assert.Equal(t, "8948874854696341437962075211230225158124203775185169948359228967178039019393", cipherText[2].Text(10)) + assert.Equal(t, "13654519867376896827561074824557193869787926453227340059166840999629617764240", cipherText[3].Text(10)) +} + +func TestPoseidonDecrypt(t *testing.T) { + x, _ := new(big.Int).SetString("2225468530552752510522780019536893048169408270351832766087923920964657502364", 10) + y, _ := new(big.Int).SetString("18264896395019517559018400396898398442219687903646821466907778802937824776999", 10) + key := []*big.Int{x, y} + nonce, _ := new(big.Int).SetString("220373351579243596212522709113509916796", 10) + c1, _ := new(big.Int).SetString("3623473636383738070031324804097049685564466189577273141109672613904615191520", 10) + c2, _ := new(big.Int).SetString("14192366070411288656840625597415252300006763456323771445497376523077328650161", 10) + c3, _ := new(big.Int).SetString("8948874854696341437962075211230225158124203775185169948359228967178039019393", 10) + c4, _ := new(big.Int).SetString("13654519867376896827561074824557193869787926453227340059166840999629617764240", 10) + cipherText := []*big.Int{c1, c2, c3, c4} + msg, err := PoseidonDecrypt(cipherText, key, nonce, 2) + assert.NoError(t, err) + assert.Equal(t, "1234567890", msg[0].Text(10)) + assert.Equal(t, "2345678901", msg[1].Text(10)) +} + +func TestPoseidonEncryptDecryptWithLongMessages_3n(t *testing.T) { + nonce := NewEncryptionNonce() + x, _ := new(big.Int).SetString("14104886431895638088879904796248988944763544789684292755064599086710742631244", 10) + y, _ := new(big.Int).SetString("12567888666920372522142016384715158971908391387943244674769979344082830343251", 10) + sharedKey := []*big.Int{x, y} + + // Encrypt a message of length 3n and bigger than 3 + msg := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901), big.NewInt(3456789012), big.NewInt(4567890123), big.NewInt(5678901234), big.NewInt(6789012345)} + cipherText, err := PoseidonEncrypt(msg, sharedKey, nonce) + assert.NoError(t, err) + assert.Equal(t, 7, len(cipherText)) + + // Decrypt the message + decryptedMsg, err := PoseidonDecrypt(cipherText, sharedKey, nonce, 6) + assert.NoError(t, err) + assert.Equal(t, 6, len(decryptedMsg)) + assert.Equal(t, "1234567890", decryptedMsg[0].Text(10)) + assert.Equal(t, "2345678901", decryptedMsg[1].Text(10)) + assert.Equal(t, "3456789012", decryptedMsg[2].Text(10)) + assert.Equal(t, "4567890123", decryptedMsg[3].Text(10)) + assert.Equal(t, "5678901234", decryptedMsg[4].Text(10)) + assert.Equal(t, "6789012345", decryptedMsg[5].Text(10)) +} + +func TestPoseidonEncryptDecryptWithLongMessages_3nPlus1(t *testing.T) { + nonce := NewEncryptionNonce() + x, _ := new(big.Int).SetString("14104886431895638088879904796248988944763544789684292755064599086710742631244", 10) + y, _ := new(big.Int).SetString("12567888666920372522142016384715158971908391387943244674769979344082830343251", 10) + sharedKey := []*big.Int{x, y} + + // Encrypt a message of length 3n+1 and bigger than 3 + msg := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901), big.NewInt(3456789012), big.NewInt(4567890123), big.NewInt(5678901234), big.NewInt(6789012345), big.NewInt(7890123456)} + cipherText, err := PoseidonEncrypt(msg, sharedKey, nonce) + assert.NoError(t, err) + assert.Equal(t, 10, len(cipherText)) + + // Decrypt the message + decryptedMsg, err := PoseidonDecrypt(cipherText, sharedKey, nonce, 7) + assert.NoError(t, err) + assert.Equal(t, 7, len(decryptedMsg)) + assert.Equal(t, "1234567890", decryptedMsg[0].Text(10)) + assert.Equal(t, "2345678901", decryptedMsg[1].Text(10)) + assert.Equal(t, "3456789012", decryptedMsg[2].Text(10)) + assert.Equal(t, "4567890123", decryptedMsg[3].Text(10)) + assert.Equal(t, "5678901234", decryptedMsg[4].Text(10)) + assert.Equal(t, "6789012345", decryptedMsg[5].Text(10)) + assert.Equal(t, "7890123456", decryptedMsg[6].Text(10)) +} + +func TestPoseidonEncryptDecryptWithLongMessages_3nPlus2(t *testing.T) { + nonce := NewEncryptionNonce() + x, _ := new(big.Int).SetString("14104886431895638088879904796248988944763544789684292755064599086710742631244", 10) + y, _ := new(big.Int).SetString("12567888666920372522142016384715158971908391387943244674769979344082830343251", 10) + sharedKey := []*big.Int{x, y} + + // Encrypt a message of length 3n+2 and bigger than 3 + msg := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901), big.NewInt(3456789012), big.NewInt(4567890123), big.NewInt(5678901234), big.NewInt(6789012345), big.NewInt(7890123456), big.NewInt(8901234567)} + cipherText, err := PoseidonEncrypt(msg, sharedKey, nonce) + assert.NoError(t, err) + assert.Equal(t, 10, len(cipherText)) + + // Decrypt the message + decryptedMsg, err := PoseidonDecrypt(cipherText, sharedKey, nonce, 8) + assert.NoError(t, err) + assert.Equal(t, 8, len(decryptedMsg)) + assert.Equal(t, "1234567890", decryptedMsg[0].Text(10)) + assert.Equal(t, "2345678901", decryptedMsg[1].Text(10)) + assert.Equal(t, "3456789012", decryptedMsg[2].Text(10)) + assert.Equal(t, "4567890123", decryptedMsg[3].Text(10)) + assert.Equal(t, "5678901234", decryptedMsg[4].Text(10)) + assert.Equal(t, "6789012345", decryptedMsg[5].Text(10)) + assert.Equal(t, "7890123456", decryptedMsg[6].Text(10)) + assert.Equal(t, "8901234567", decryptedMsg[7].Text(10)) +} + +func TestPoseidonEncryptFail_WrongKeyLength(t *testing.T) { + nonce := NewEncryptionNonce() + x, _ := new(big.Int).SetString("14104886431895638088879904796248988944763544789684292755064599086710742631244", 10) + y, _ := new(big.Int).SetString("12567888666920372522142016384715158971908391387943244674769979344082830343251", 10) + sharedKey := []*big.Int{x, y, big.NewInt(0)} + + msg := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901), big.NewInt(3456789012)} + _, err := PoseidonEncrypt(msg, sharedKey, nonce) + assert.EqualError(t, err, "the key must have 2 elements, but got 3") +} + +func TestPoseidonDecryptFail_WrongKeyLength(t *testing.T) { + nonce := NewEncryptionNonce() + x, _ := new(big.Int).SetString("14104886431895638088879904796248988944763544789684292755064599086710742631244", 10) + y, _ := new(big.Int).SetString("12567888666920372522142016384715158971908391387943244674769979344082830343251", 10) + sharedKey := []*big.Int{x, y, big.NewInt(0)} + + cipherText := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901), big.NewInt(3456789012)} + _, err := PoseidonDecrypt(cipherText, sharedKey, nonce, 2) + assert.EqualError(t, err, "the key must have 2 elements, but got 3") +} + +func TestPoseidonDecryptFail_WrongCipherTextLength(t *testing.T) { + nonce := NewEncryptionNonce() + x, _ := new(big.Int).SetString("14104886431895638088879904796248988944763544789684292755064599086710742631244", 10) + y, _ := new(big.Int).SetString("12567888666920372522142016384715158971908391387943244674769979344082830343251", 10) + sharedKey := []*big.Int{x, y} + + cipherText := []*big.Int{big.NewInt(0), big.NewInt(0), big.NewInt(0)} + _, err := PoseidonDecrypt(cipherText, sharedKey, nonce, 3) + assert.EqualError(t, err, "the length of the cipher text must be 3n+1, but got 3") +} + +func TestPoseidonDecryptFail_DecryptedPaddingNotZero(t *testing.T) { + // using the wrong key to decrypt the message to cause the padding to be not zero + x, _ := new(big.Int).SetString("2225468530552752510522780019536893048169408270351832766087923920964657502364", 10) + y, _ := new(big.Int).SetString("18264896395019517559018400396898398442219687903646821466907778802937824776999", 10) + key := []*big.Int{x, y} + nonce, _ := new(big.Int).SetString("220373351579243596212522709113509916796", 10) + c1, _ := new(big.Int).SetString("21039212344699161871898848613539390724983078519593720831154100738838838817785", 10) + c2, _ := new(big.Int).SetString("15977345191053612091391791983324450668689493803418876942389476888978407035452", 10) + c3, _ := new(big.Int).SetString("5372411734880755416968088645882790397762440240735300976022902570974350057620", 10) + c4, _ := new(big.Int).SetString("19025632437627264551396791396969817959604751019734376072608658820773266990048", 10) + c5, _ := new(big.Int).SetString("6160222253936480678964609308357479211911264527083645332807503357507880448565", 10) + c6, _ := new(big.Int).SetString("20422614434004746360815436868365648789543143774057224246303845550093575638210", 10) + c7, _ := new(big.Int).SetString("7221629992444548069963332349434479366564733367858828912252976650571445651163", 10) + + cipherText := []*big.Int{c1, c2, c3, c4, c5, c6, c7} + _, err := PoseidonDecrypt(cipherText, key, nonce, 4) + assert.EqualError(t, err, "the last two elements of the decrypted text must be 0") + + _, err = PoseidonDecrypt(cipherText, key, nonce, 5) + assert.EqualError(t, err, "the last element of the decrypted text must be 0") +} + +func TestPoseidonDecryptFail_LastCipherTextCheck(t *testing.T) { + x, _ := new(big.Int).SetString("2225468530552752510522780019536893048169408270351832766087923920964657502364", 10) + y, _ := new(big.Int).SetString("18264896395019517559018400396898398442219687903646821466907778802937824776999", 10) + key := []*big.Int{x, y} + nonce, _ := new(big.Int).SetString("220373351579243596212522709113509916796", 10) + c1, _ := new(big.Int).SetString("3623473636383738070031324804097049685564466189577273141109672613904615191520", 10) + c2, _ := new(big.Int).SetString("14192366070411288656840625597415252300006763456323771445497376523077328650161", 10) + c3, _ := new(big.Int).SetString("8948874854696341437962075211230225158124203775185169948359228967178039019393", 10) + c4, _ := new(big.Int).SetString("13654519867376896827561074824557193869787926453227340059166840999629617764240", 10) + c4 = c4.Add(c4, big.NewInt(1)) + cipherText := []*big.Int{c1, c2, c3, c4} + _, err := PoseidonDecrypt(cipherText, key, nonce, 2) + assert.EqualError(t, err, "the last element of the cipher text does not match the 2nd element of the state from the last round") +} + +func TestPoseidonEncryptFail_NonceTooBig(t *testing.T) { + nonce := new(big.Int).Lsh(big.NewInt(1), 128) + x, _ := new(big.Int).SetString("2225468530552752510522780019536893048169408270351832766087923920964657502364", 10) + y, _ := new(big.Int).SetString("18264896395019517559018400396898398442219687903646821466907778802937824776999", 10) + sharedKey := []*big.Int{x, y} + + msg := []*big.Int{big.NewInt(1234567890), big.NewInt(2345678901)} + _, err := PoseidonEncrypt(msg, sharedKey, nonce) + assert.EqualError(t, err, "the nonce must be less than 2^128, but got 340282366920938463463374607431768211456") +} + +func TestPoseidonDecryptFail_NonceTooBig(t *testing.T) { + nonce := new(big.Int).Lsh(big.NewInt(1), 128) + x, _ := new(big.Int).SetString("2225468530552752510522780019536893048169408270351832766087923920964657502364", 10) + y, _ := new(big.Int).SetString("18264896395019517559018400396898398442219687903646821466907778802937824776999", 10) + sharedKey := []*big.Int{x, y} + + c1, _ := new(big.Int).SetString("3623473636383738070031324804097049685564466189577273141109672613904615191520", 10) + c2, _ := new(big.Int).SetString("14192366070411288656840625597415252300006763456323771445497376523077328650161", 10) + c3, _ := new(big.Int).SetString("8948874854696341437962075211230225158124203775185169948359228967178039019393", 10) + c4, _ := new(big.Int).SetString("13654519867376896827561074824557193869787926453227340059166840999629617764240", 10) + cipherText := []*big.Int{c1, c2, c3, c4} + _, err := PoseidonDecrypt(cipherText, sharedKey, nonce, 2) + assert.EqualError(t, err, "the nonce must be less than 2^128, but got 340282366920938463463374607431768211456") +} + +func TestPoseidonDecryptFail_LengthOutOfRange(t *testing.T) { + nonce := NewEncryptionNonce() + x, _ := new(big.Int).SetString("2225468530552752510522780019536893048169408270351832766087923920964657502364", 10) + y, _ := new(big.Int).SetString("18264896395019517559018400396898398442219687903646821466907778802937824776999", 10) + sharedKey := []*big.Int{x, y} + + c1, _ := new(big.Int).SetString("3623473636383738070031324804097049685564466189577273141109672613904615191520", 10) + c2, _ := new(big.Int).SetString("14192366070411288656840625597415252300006763456323771445497376523077328650161", 10) + c3, _ := new(big.Int).SetString("8948874854696341437962075211230225158124203775185169948359228967178039019393", 10) + c4, _ := new(big.Int).SetString("13654519867376896827561074824557193869787926453227340059166840999629617764240", 10) + cipherText := []*big.Int{c1, c2, c3, c4} + _, err := PoseidonDecrypt(cipherText, sharedKey, nonce, 0) + assert.EqualError(t, err, "the length must be between 1 and 3 (length of cipher text array - 1), but got 0") + + _, err = PoseidonDecrypt(cipherText, sharedKey, nonce, 5) + assert.EqualError(t, err, "the length must be between 1 and 3 (length of cipher text array - 1), but got 5") +} diff --git a/go-sdk/internal/utxo/utils.go b/go-sdk/internal/crypto/utils.go similarity index 99% rename from go-sdk/internal/utxo/utils.go rename to go-sdk/internal/crypto/utils.go index db5f2cc..044c0d9 100644 --- a/go-sdk/internal/utxo/utils.go +++ b/go-sdk/internal/crypto/utils.go @@ -14,7 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package utxo +package crypto import ( "crypto/rand" diff --git a/go-sdk/internal/utxo/utils_test.go b/go-sdk/internal/crypto/utils_test.go similarity index 98% rename from go-sdk/internal/utxo/utils_test.go rename to go-sdk/internal/crypto/utils_test.go index 67df624..354563a 100644 --- a/go-sdk/internal/utxo/utils_test.go +++ b/go-sdk/internal/crypto/utils_test.go @@ -14,7 +14,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -package utxo +package crypto import ( "math/big" diff --git a/go-sdk/internal/sparse-merkle-tree/smt/smt_test.go b/go-sdk/internal/sparse-merkle-tree/smt/smt_test.go index 6d48ad0..11ab4d0 100644 --- a/go-sdk/internal/sparse-merkle-tree/smt/smt_test.go +++ b/go-sdk/internal/sparse-merkle-tree/smt/smt_test.go @@ -23,11 +23,11 @@ import ( "os" "testing" + "github.com/hyperledger-labs/zeto/go-sdk/internal/crypto" "github.com/hyperledger-labs/zeto/go-sdk/internal/sparse-merkle-tree/node" "github.com/hyperledger-labs/zeto/go-sdk/internal/sparse-merkle-tree/storage" "github.com/hyperledger-labs/zeto/go-sdk/internal/testutils" "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/utxo" "github.com/iden3/go-iden3-crypto/babyjub" "github.com/stretchr/testify/assert" "gorm.io/driver/sqlite" @@ -230,7 +230,7 @@ func TestSqliteStorage(t *testing.T) { uriString := "https://example.com/token/1001" assert.NoError(t, err) sender := testutils.NewKeypair() - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() utxo1 := node.NewNonFungible(tokenId, uriString, sender.PublicKey, salt1) n1, err := node.NewLeafNode(utxo1) diff --git a/go-sdk/internal/sparse-merkle-tree/storage/sql_test.go b/go-sdk/internal/sparse-merkle-tree/storage/sql_test.go index a23cd3d..6d271a2 100644 --- a/go-sdk/internal/sparse-merkle-tree/storage/sql_test.go +++ b/go-sdk/internal/sparse-merkle-tree/storage/sql_test.go @@ -21,10 +21,10 @@ import ( "os" "testing" + "github.com/hyperledger-labs/zeto/go-sdk/internal/crypto" "github.com/hyperledger-labs/zeto/go-sdk/internal/sparse-merkle-tree/node" "github.com/hyperledger-labs/zeto/go-sdk/internal/testutils" "github.com/hyperledger-labs/zeto/go-sdk/pkg/sparse-merkle-tree/core" - "github.com/hyperledger-labs/zeto/go-sdk/pkg/utxo" "github.com/stretchr/testify/assert" "gorm.io/driver/sqlite" "gorm.io/gorm" @@ -61,7 +61,7 @@ func TestSqliteStorage(t *testing.T) { uriString := "https://example.com/token/1001" assert.NoError(t, err) sender := testutils.NewKeypair() - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() utxo1 := node.NewNonFungible(tokenId, uriString, sender.PublicKey, salt1) n1, err := node.NewLeafNode(utxo1) @@ -167,7 +167,7 @@ func TestSqliteStorageFail_BadNodeIndex(t *testing.T) { assert.NoError(t, err) sender := testutils.NewKeypair() - salt1 := utxo.NewSalt() + salt1 := crypto.NewSalt() utxo1 := node.NewFungible(big.NewInt(100), sender.PublicKey, salt1) n1, err := node.NewLeafNode(utxo1) diff --git a/go-sdk/pkg/crypto/crypto.go b/go-sdk/pkg/crypto/crypto.go new file mode 100644 index 0000000..b25a149 --- /dev/null +++ b/go-sdk/pkg/crypto/crypto.go @@ -0,0 +1,46 @@ +// Copyright © 2024 Kaleido, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package crypto + +import ( + "math/big" + + "github.com/hyperledger-labs/zeto/go-sdk/internal/crypto" + "github.com/iden3/go-iden3-crypto/babyjub" +) + +// NewSalt generates a new random salt in the range of [0, MAX) where MAX is the order of the BabyJub curve. +// This ensures that the salt is a valid scalar for the curve. +func NewSalt() *big.Int { + return crypto.NewSalt() +} + +func NewEncryptionNonce() *big.Int { + return crypto.NewEncryptionNonce() +} + +func PoseidonEncrypt(msg []*big.Int, key []*big.Int, nonce *big.Int) ([]*big.Int, error) { + return crypto.PoseidonEncrypt(msg, key, nonce) +} + +func PoseidonDecrypt(ciphertext []*big.Int, key []*big.Int, nonce *big.Int, length int) ([]*big.Int, error) { + return crypto.PoseidonDecrypt(ciphertext, key, nonce, length) +} + +func GenerateECDHSharedSecret(privKey *babyjub.PrivateKey, pubKey *babyjub.PublicKey) *babyjub.Point { + return crypto.GenerateECDHSharedSecret(privKey, pubKey) +} diff --git a/go-sdk/pkg/utxo/utxo.go b/go-sdk/pkg/utxo/utxo.go index 62f9095..548e2a7 100644 --- a/go-sdk/pkg/utxo/utxo.go +++ b/go-sdk/pkg/utxo/utxo.go @@ -43,13 +43,3 @@ func NewNonFungibleNullifier(tokenId *big.Int, tokenUri string, owner *big.Int, func HashTokenUri(tokenUri string) (*big.Int, error) { return utxo.HashTokenUri(tokenUri) } - -// NewSalt generates a new random salt in the range of [0, MAX) where MAX is the order of the BabyJub curve. -// This ensures that the salt is a valid scalar for the curve. -func NewSalt() *big.Int { - return utxo.NewSalt() -} - -func NewEncryptionNonce() *big.Int { - return utxo.NewEncryptionNonce() -}