From c9e4aa972fbdac57906d69c5fc3636f33a52960a Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Mon, 18 Dec 2023 14:33:44 +0000 Subject: [PATCH 1/4] fix(weaver-fabric-node-sdk): made AES key length configurable in ECIES functions The Weaver Fabric interoperation-node-sdk used the "aes-128-ctr" algorithm in a hardcoded manner for asymmetric encryption/decryption. 128-bit AES, though secure for classical computing, is quantum-unsafe. So an option is added to use "aes-256-ctr" on demand. The "aes-128-ctr" algorithm is still supported because many signing keys, typically those used in the Fabric testnets, have embedded elliptic curve parameters with key length 16 bytes, and those still need to be supported. Signed-off-by: VRamakrishna --- .../src/eciesCrypto.js | 34 ++++++++++++++----- .../test/InteroperableHelper.js | 24 ++++++++++++- 2 files changed, 49 insertions(+), 9 deletions(-) diff --git a/weaver/sdks/fabric/interoperation-node-sdk/src/eciesCrypto.js b/weaver/sdks/fabric/interoperation-node-sdk/src/eciesCrypto.js index 70496955eb..082084fcc1 100644 --- a/weaver/sdks/fabric/interoperation-node-sdk/src/eciesCrypto.js +++ b/weaver/sdks/fabric/interoperation-node-sdk/src/eciesCrypto.js @@ -61,7 +61,7 @@ const CURVE_P_384_Size = 384; * Comments below indicate a mapping between variables defined here and in the * 'Decrypt(...)' function implemented in 'ecies.go' in the Golang package */ -function eciesDecryptMessage(recipientPrivateKey, cipherText, options) { +function eciesDecryptMessage(recipientPrivateKey, cipherText, options, aesKeyLength = AESKeyLength) { const level = recipientPrivateKey.ecparams.keylen; options.securityLevel = level; processOption(options); @@ -96,8 +96,8 @@ function eciesDecryptMessage(recipientPrivateKey, cipherText, options) { const kdfOutput = hkdf(ZArray, ECIESKDFOutput, null, null, options); const kbuf = Buffer.from(kdfOutput); - const aesKey = kdfOutput.slice(0, AESKeyLength); // 'Ke' - const hmacKey = kdfOutput.slice(AESKeyLength, AESKeyLength + HMACKeyLength); + const aesKey = kdfOutput.slice(0, aesKeyLength); // 'Ke' + const hmacKey = kdfOutput.slice(aesKeyLength, aesKeyLength + HMACKeyLength); const hmacKeyHash = new options.hashFunctionKeyDerivation(); hmacKeyHash.update(bytesToBits(hmacKey)); @@ -108,7 +108,16 @@ function eciesDecryptMessage(recipientPrivateKey, cipherText, options) { throw new Error("HMAC verify failed"); } const iv = EM.slice(0, IVLength); - const cipher = crypto.createDecipheriv("aes-128-ctr", Buffer.from(aesKey), iv); // The Golang package implements AES-128-CTR + // The go-ethereum crypto/ecies package that encrypts/decrypts data uses AES-128-CTR by default for signing certs currently generated by Fabric + let aesAlgorithm; + if (aesKeyLength === 16) { + aesAlgorithm = "aes-128-ctr"; + } else if (aesKeyLength === 32) { + aesAlgorithm = "aes-256-ctr"; + } else { + throw new Error("Invalid AES key length supplied: " + aesKeyLength); + } + const cipher = crypto.createDecipheriv(aesAlgorithm, Buffer.from(aesKey), iv); const decryptedBytes = cipher.update(EM.slice(IVLength)); return decryptedBytes; } @@ -135,7 +144,7 @@ function eciesDecryptMessage(recipientPrivateKey, cipherText, options) { * @returns encrypted message as a Buffer. * */ -function eciesEncryptMessage(recipientPublicKey, msg, options) { +function eciesEncryptMessage(recipientPublicKey, msg, options, aesKeyLength = AESKeyLength) { const level = recipientPublicKey.ecparams.keylen; options.securityLevel = level; processOption(options); @@ -153,15 +162,24 @@ function eciesEncryptMessage(recipientPublicKey, msg, options) { const Z = ephPrivKey.derive(pubKey.pub); const kdfOutput = hkdf(Z.toArray(), ECIESKDFOutput, null, null, options); - const aesKey = kdfOutput.slice(0, AESKeyLength); - const hmacKey = kdfOutput.slice(AESKeyLength, AESKeyLength + HMACKeyLength); + const aesKey = kdfOutput.slice(0, aesKeyLength); + const hmacKey = kdfOutput.slice(aesKeyLength, aesKeyLength + HMACKeyLength); const hmacKeyHash = new options.hashFunctionKeyDerivation(); hmacKeyHash.update(bytesToBits(hmacKey)); const hKm = bitsToBytes(hmacKeyHash.finalize()); const iv = crypto.randomBytes(IVLength); - const cipher = crypto.createCipheriv("aes-256-ctr", Buffer.from(aesKey), iv); + // The go-ethereum crypto/ecies package that encrypts/decrypts data uses AES-128-CTR by default for signing certs currently generated by Fabric + let aesAlgorithm; + if (aesKeyLength === 16) { + aesAlgorithm = "aes-128-ctr"; + } else if (aesKeyLength === 32) { + aesAlgorithm = "aes-256-ctr"; + } else { + throw new Error("Invalid AES key length supplied: " + aesKeyLength); + } + const cipher = crypto.createCipheriv(aesAlgorithm, Buffer.from(aesKey), iv); const encryptedBytes = cipher.update(msg); const EM = Buffer.concat([iv, encryptedBytes]); const D = hmac(hKm, EM, options); diff --git a/weaver/sdks/fabric/interoperation-node-sdk/test/InteroperableHelper.js b/weaver/sdks/fabric/interoperation-node-sdk/test/InteroperableHelper.js index 8ebc612ca1..2b2f3a808b 100644 --- a/weaver/sdks/fabric/interoperation-node-sdk/test/InteroperableHelper.js +++ b/weaver/sdks/fabric/interoperation-node-sdk/test/InteroperableHelper.js @@ -107,7 +107,7 @@ describe("InteroperableHelper", () => { }); describe("cryptographic functions", () => { - it("encrypt and decrypt a message", () => { + it("encrypt and decrypt a message with default 128-bit AES key", () => { const data = '{ "data": "xyz" }'; const privKeyFile = `${__dirname}/data/privKey.pem`; const privKeyPEM = fs.readFileSync(privKeyFile).toString(); @@ -128,6 +128,28 @@ describe("InteroperableHelper", () => { }); }); + describe("cryptographic functions", () => { + it("encrypt and decrypt a message with 256-bit AES key", () => { + const data = '{ "data": "xyz" }'; + const privKeyFile = `${__dirname}/data/privKey.pem`; + const privKeyPEM = fs.readFileSync(privKeyFile).toString(); + const privKey = keyutil.getKeyFromPlainPrivatePKCS8PEM(privKeyPEM); + const signCertFile = `${__dirname}/data/signCert.pem`; + const signCertPEM = fs.readFileSync(signCertFile).toString(); + const pubKey = keyutil.getKey(signCertPEM); + const cryptoOptions = { hashAlgorithm: "SHA2" }; + expect(() => { + const encryptedData = ecies.eciesEncryptMessage(pubKey, Buffer.from(data), cryptoOptions, 32); // 32 * 8 == 256 + expect(encryptedData).to.be.a("Uint8Array"); + expect(() => { + const decryptedData = ecies.eciesDecryptMessage(privKey, encryptedData, cryptoOptions, 32); // 32 * 8 == 256 + expect(decryptedData).to.be.a("Uint8Array"); + expect(decryptedData.toString()).to.equal(data); + }).to.not.throw(); + }).to.not.throw(); + }); + }); + describe("decrypt remote proposal response", () => { it("decrypt proposal response using private key", () => { const samplePropJSON = JSON.parse(fs.readFileSync(`${__dirname}/data/prop.json`).toString()); From 284c600f16e991544324d2414c4272b01c7c9daf Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Wed, 20 Dec 2023 11:03:09 +0000 Subject: [PATCH 2/4] fix(weaver-membership-functions): reverted earlier buggy change affecting identity mgmt Partially reverting changes made in https://github.com/hyperledger/cacti/pull/2901. These changes made the IIN Agent chaincode operations fail. Also fixed typos and formatting in the docs. Signed-off-by: VRamakrishna --- .../getting-started/test-network/ledger-initialization.md | 3 +++ .../getting-started/test-network/setup-local-docker.md | 4 +++- .../docs/weaver/getting-started/test-network/setup-local.md | 1 + .../getting-started/test-network/setup-packages-docker.md | 1 + .../fabric-interop-cc/contracts/interop/membership.go | 2 -- .../src/helpers/interop-setup/configure-network.ts | 4 ++-- .../fabric/interoperation-node-sdk/src/MembershipManager.ts | 6 ++++-- 7 files changed, 14 insertions(+), 7 deletions(-) diff --git a/docs/docs/weaver/getting-started/test-network/ledger-initialization.md b/docs/docs/weaver/getting-started/test-network/ledger-initialization.md index f6c6fd3b3e..b2bdc78d28 100644 --- a/docs/docs/weaver/getting-started/test-network/ledger-initialization.md +++ b/docs/docs/weaver/getting-started/test-network/ledger-initialization.md @@ -69,6 +69,7 @@ Prepare `fabric-cli` for configuration suitably as follows. ```bash ./bin/fabric-cli env set-file ./.env ``` + | Notes | |:------| | If the `CONFIG_PATH` environment variable is omitted from `.env`, then you must also run:
```./bin/fabric-cli config set-file ./config.json``` | @@ -166,6 +167,7 @@ Prepare `fabric-cli` for configuration suitably as follows. ```bash ./bin/fabric-cli env set-file ./.env ``` + | Notes | |:------| | If the `CONFIG_PATH` environment variable is omitted from `.env`, then you must also run:
```./bin/fabric-cli config set-file ./config.json``` | @@ -283,6 +285,7 @@ Prepare `fabric-cli` for configuration suitably as follows. ``` ./bin/fabric-cli env set-file ./.env ``` + | Notes | |:------| | If the `CONFIG_PATH` environment variable is omitted from `.env`, then you must also run:
```./bin/fabric-cli config set-file ./config.json``` | diff --git a/docs/docs/weaver/getting-started/test-network/setup-local-docker.md b/docs/docs/weaver/getting-started/test-network/setup-local-docker.md index 72e5e6b88f..c0475d4a8d 100644 --- a/docs/docs/weaver/getting-started/test-network/setup-local-docker.md +++ b/docs/docs/weaver/getting-started/test-network/setup-local-docker.md @@ -49,6 +49,7 @@ Before starting, make sure you have the following software installed on your hos go install google.golang.org/protobuf/cmd/protoc-gen-go go install google.golang.org/grpc/cmd/protoc-gen-go-grpc ``` + | Notes | |:------| | The latest version at present is `3.15.6`, but you should check the above link to find the most current version before running the above steps. | @@ -273,6 +274,7 @@ make build-image-local #### Deployment Use the following steps to run Fabric IIN Agents in Docker containers: + * The `.env.n1.org1` and `.env.n1.org1.tls` files in the `docker-testnet/envs` directory contain environment variables used by the iin-agent of `org1` of `network1` at startup and runtime. Edit either of these files (depending on whether you wish to start the relay with or without TLS) as follows: - Replace `` with the absolute path of the `weaver` folder within your Cacti repository clone. - Update the following value: @@ -439,7 +441,7 @@ Navigate to the `weaver/core/relay` folder. Refer [here](#building-relay-image) ``` * The `.env.corda` and `.env.corda.tls` files in the `docker/testnet-envs` directory contain environment variables used by the `Corda_Network` relay at startup and runtime. Edit either of these files (depending on whether you wish to start the relay with or without TLS), and update the following value: ``` - DOCKER_IMAGE_NAME=weaver-relay-server + DOCKER_IMAGE_NAME=cacti-weaver-relay-server ``` * Repeat the above step for `.env.corda2` or `.env.corda2.tls` in `docker/testnet-envs` directory, which contain environment variables for the `Corda_Network2` relay. * To deploy the relay server for `Corda_Network` without TLS, run: diff --git a/docs/docs/weaver/getting-started/test-network/setup-local.md b/docs/docs/weaver/getting-started/test-network/setup-local.md index 90cdd46b72..820c724313 100644 --- a/docs/docs/weaver/getting-started/test-network/setup-local.md +++ b/docs/docs/weaver/getting-started/test-network/setup-local.md @@ -51,6 +51,7 @@ Before starting, make sure you have the following software installed on your hos go install google.golang.org/protobuf/cmd/protoc-gen-go go install google.golang.org/grpc/cmd/protoc-gen-go-grpc ``` + | Notes | |:------| | The latest version at present is `3.15.6`, but you should check the above link to find the most current version before running the above steps. | diff --git a/docs/docs/weaver/getting-started/test-network/setup-packages-docker.md b/docs/docs/weaver/getting-started/test-network/setup-packages-docker.md index e7a8bb955f..ecc1dab777 100644 --- a/docs/docs/weaver/getting-started/test-network/setup-packages-docker.md +++ b/docs/docs/weaver/getting-started/test-network/setup-packages-docker.md @@ -172,6 +172,7 @@ IIN Agent is a client of a member of a DLT network or security domain with speci #### Deployment Use the following steps to run Fabric IIN Agents in Docker containers: + * The `.env.n1.org1` and `.env.n1.org1.tls` files in the `docker-testnet/envs` directory contain environment variables used by the iin-agent of `org1` of `network1` at startup and runtime. Edit either of these files (depending on whether you wish to start the relay with or without TLS) as follows: - Replace `` with the absolute path of the `weaver` folder within your Cacti repository clone. - If Fabric network was started with 1 org, and IIN Agents are to be started with TLS enabled, update the `DNS_CONFIG_PATH` variable as: diff --git a/weaver/core/network/fabric-interop-cc/contracts/interop/membership.go b/weaver/core/network/fabric-interop-cc/contracts/interop/membership.go index 6ad8978f12..bae0ec6d87 100644 --- a/weaver/core/network/fabric-interop-cc/contracts/interop/membership.go +++ b/weaver/core/network/fabric-interop-cc/contracts/interop/membership.go @@ -183,7 +183,6 @@ func (s *SmartContract) CreateLocalMembership(ctx contractapi.TransactionContext if err != nil { return fmt.Errorf("Unmarshal error: %s", err) } - membership.SecurityDomain = membershipLocalSecurityDomain membershipLocalKey, err := ctx.GetStub().CreateCompositeKey(membershipObjectType, []string{membershipLocalSecurityDomain}) acp, getErr := ctx.GetStub().GetState(membershipLocalKey) @@ -221,7 +220,6 @@ func (s *SmartContract) UpdateLocalMembership(ctx contractapi.TransactionContext if err != nil { return fmt.Errorf("Unmarshal error: %s", err) } - membership.SecurityDomain = membershipLocalSecurityDomain membershipLocalKey, err := ctx.GetStub().CreateCompositeKey(membershipObjectType, []string{membershipLocalSecurityDomain}) _, getErr := s.GetMembershipBySecurityDomain(ctx, membershipLocalSecurityDomain) diff --git a/weaver/samples/fabric/fabric-cli/src/helpers/interop-setup/configure-network.ts b/weaver/samples/fabric/fabric-cli/src/helpers/interop-setup/configure-network.ts index 3a33ad4ea5..282a8e1aee 100644 --- a/weaver/samples/fabric/fabric-cli/src/helpers/interop-setup/configure-network.ts +++ b/weaver/samples/fabric/fabric-cli/src/helpers/interop-setup/configure-network.ts @@ -142,12 +142,12 @@ const loadLocalHelper = async ( registerUser: false }) try { - const response = await MembershipManager.createLocalMembership(gateway, members, channelName, contractName) + const response = await MembershipManager.createLocalMembership(gateway, members, networkName, channelName, contractName) logger.info('CreateLocalMembership Successful.') } catch (e) { logger.error(e) logger.info('CreateLocalMembership attempting Update') - const response = await MembershipManager.updateLocalMembership(gateway, members, channelName, contractName) + const response = await MembershipManager.updateLocalMembership(gateway, members, networkName, channelName, contractName) logger.info('UpdateLocalMembership response: success: ', response) } } diff --git a/weaver/sdks/fabric/interoperation-node-sdk/src/MembershipManager.ts b/weaver/sdks/fabric/interoperation-node-sdk/src/MembershipManager.ts index b0bf218d98..ca38534c82 100644 --- a/weaver/sdks/fabric/interoperation-node-sdk/src/MembershipManager.ts +++ b/weaver/sdks/fabric/interoperation-node-sdk/src/MembershipManager.ts @@ -22,12 +22,13 @@ import { handlePromise, promisifyAll } from './helpers' async function createLocalMembership( gateway: Gateway, memberMspIds: Array, + securityDomain: string, channelName: string, weaverCCId: string ): Promise { const network = await gateway.getNetwork(channelName) const membership = getMSPConfigurations(network, memberMspIds) - membership.setSecuritydomain('') + membership.setSecuritydomain(securityDomain) const membership64 = Buffer.from(membership.serializeBinary()).toString('base64') const contract = network.getContract(weaverCCId) return await contract.submitTransaction("CreateLocalMembership", membership64); @@ -36,12 +37,13 @@ async function createLocalMembership( async function updateLocalMembership( gateway: Gateway, memberMspIds: Array, + securityDomain: string, channelName: string, weaverCCId: string ): Promise { const network = await gateway.getNetwork(channelName) const membership = getMSPConfigurations(network, memberMspIds) - membership.setSecuritydomain('') + membership.setSecuritydomain(securityDomain) const membership64 = Buffer.from(membership.serializeBinary()).toString('base64') const contract = network.getContract(weaverCCId) return await contract.submitTransaction("UpdateLocalMembership", membership64); From 78d7d17033ce9d8bb92225b40e15da9a8d32f69c Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Wed, 20 Dec 2023 11:26:04 +0000 Subject: [PATCH 3/4] docs(weaver-tests): updated download version instruction for Besu testnet Signed-off-by: VRamakrishna --- weaver/tests/network-setups/besu/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/weaver/tests/network-setups/besu/README.md b/weaver/tests/network-setups/besu/README.md index c704dffd81..7e19bbab97 100644 --- a/weaver/tests/network-setups/besu/README.md +++ b/weaver/tests/network-setups/besu/README.md @@ -10,7 +10,7 @@ - Besu: * Download and unpack the latest https://github.com/hyperledger/besu/releases/latest. You will find it in the link of the form: https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/x.y.z/besu-x.y.z.zip with x.y.z replaced with the version number. For instance, run the following command after updating version number accordingly. ``` -wget https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/21.7.0/besu-21.7.0.zip +wget https://hyperledger.jfrog.io/artifactory/besu-binaries/besu/23.10.2/besu-23.10.2.zip ``` * Add the path to besu-x.y.z/bin to PATH - EthSigner: From b6b9e156226d206dd7efd33e7818302dad45a801 Mon Sep 17 00:00:00 2001 From: VRamakrishna Date: Wed, 20 Dec 2023 18:02:09 +0000 Subject: [PATCH 4/4] fix(weaver-go-sdk): corrected membership API function signatures Modified the SDK functions to be compatible with the Weaver Fabric Interoperation Chaincode. Signed-off-by: VRamakrishna --- .../fabric/go-sdk/membershipmanager/membership_manager.go | 8 ++++---- weaver/sdks/fabric/go-sdk/readme.md | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/weaver/sdks/fabric/go-sdk/membershipmanager/membership_manager.go b/weaver/sdks/fabric/go-sdk/membershipmanager/membership_manager.go index 654be316e5..e7c0bfdaaf 100644 --- a/weaver/sdks/fabric/go-sdk/membershipmanager/membership_manager.go +++ b/weaver/sdks/fabric/go-sdk/membershipmanager/membership_manager.go @@ -35,12 +35,12 @@ import ( ) -func CreateLocalMembership(walletPath, userName, connectionProfilePath, channelId, weaverCCId string, mspIds []string) error { +func CreateLocalMembership(walletPath, userName, connectionProfilePath, securityDomain, channelId, weaverCCId string, mspIds []string) error { membership, err := GetMSPConfigurations(walletPath, userName, connectionProfilePath, channelId, mspIds) if err != nil { return err } - membership.SecurityDomain = "" // We don't need this as the Weaver chaincode will internally use a designated keyword + membership.SecurityDomain = securityDomain membershipBytes, err := protoV2.Marshal(membership) if err != nil { return err @@ -55,12 +55,12 @@ func CreateLocalMembership(walletPath, userName, connectionProfilePath, channelI return nil } -func UpdateLocalMembership(walletPath, userName, connectionProfilePath, channelId, weaverCCId string, mspIds []string) error { +func UpdateLocalMembership(walletPath, userName, connectionProfilePath, securityDomain, channelId, weaverCCId string, mspIds []string) error { membership, err := GetMSPConfigurations(walletPath, userName, connectionProfilePath, channelId, mspIds) if err != nil { return err } - membership.SecurityDomain = "" // We don't need this as the Weaver chaincode will internally use a designated keyword + membership.SecurityDomain = securityDomain membershipBytes, err := protoV2.Marshal(membership) if err != nil { return err diff --git a/weaver/sdks/fabric/go-sdk/readme.md b/weaver/sdks/fabric/go-sdk/readme.md index 843cb54a69..2224bda5bb 100644 --- a/weaver/sdks/fabric/go-sdk/readme.md +++ b/weaver/sdks/fabric/go-sdk/readme.md @@ -45,11 +45,11 @@ The [membership manager functions](./membershipmanager) are currently not covere fmt.Printf("%+v\n", membership) fmt.Printf("Create Local Membership: ") - err := CreateLocalMembership(walletPath, userName, connectionProfilePath, "mychannel", "interop", []string{"Org1MSP"}) + err := CreateLocalMembership(walletPath, userName, connectionProfilePath, "network1", "mychannel", "interop", []string{"Org1MSP"}) fmt.Println(err) fmt.Printf("Update Local Membership: ") - err = UpdateLocalMembership(walletPath, userName, connectionProfilePath, "mychannel", "interop", []string{"Org1MSP"}) + err = UpdateLocalMembership(walletPath, userName, connectionProfilePath, "network1", "mychannel", "interop", []string{"Org1MSP"}) fmt.Println(err) fmt.Printf("Read Local Membership: ")