Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(weaver-fabric-node-sdk): made AES key length configurable in ECIES functions #2953

Merged
merged 4 commits into from
Dec 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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:<br/>```./bin/fabric-cli config set-file ./config.json``` |
Expand Down Expand Up @@ -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:<br/>```./bin/fabric-cli config set-file ./config.json``` |
Expand Down Expand Up @@ -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:<br/>```./bin/fabric-cli config set-file ./config.json``` |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down Expand Up @@ -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 `<PATH-TO-WEAVER>` with the absolute path of the `weaver` folder within your Cacti repository clone.
- Update the following value:
Expand Down Expand Up @@ -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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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. |
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 `<PATH-TO-WEAVER>` 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:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions weaver/sdks/fabric/go-sdk/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -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: ")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,13 @@ import { handlePromise, promisifyAll } from './helpers'
async function createLocalMembership(
gateway: Gateway,
memberMspIds: Array<string>,
securityDomain: string,
channelName: string,
weaverCCId: string
): Promise<any> {
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);
Expand All @@ -36,12 +37,13 @@ async function createLocalMembership(
async function updateLocalMembership(
gateway: Gateway,
memberMspIds: Array<string>,
securityDomain: string,
channelName: string,
weaverCCId: string
): Promise<any> {
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);
Expand Down
34 changes: 26 additions & 8 deletions weaver/sdks/fabric/interoperation-node-sdk/src/eciesCrypto.js
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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));
Expand All @@ -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;
}
Expand All @@ -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);
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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());
Expand Down
2 changes: 1 addition & 1 deletion weaver/tests/network-setups/besu/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
Loading