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());