test/utils/Encryption.spec.js
import path from "path";
import nock from "nock";
import fs from "fs";
import chai from "chai";
import Encryption from "../../src/utils/Encryption";
chai.should();
/** @test {Encryption} */
describe("utils/Encryption", () => {
/** @test {Encryption#constructor} */
describe("constructor()", () => {
/** @test {Encryption#constructor} */
it("should set default values for encryption properties", () => {
const encryption = new Encryption("clientPrivateKeySetLocation", "hyperwalletKeySetLocation");
encryption.clientPrivateKeySetLocation.should.be.equal("clientPrivateKeySetLocation");
encryption.hyperwalletKeySetLocation.should.be.equal("hyperwalletKeySetLocation");
encryption.encryptionAlgorithm.should.be.equal("RSA-OAEP-256");
encryption.signAlgorithm.should.be.equal("RS256");
encryption.encryptionMethod.should.be.equal("A256CBC-HS512");
encryption.jwsExpirationMinutes.should.be.equal(5);
});
/** @test {Encryption#constructor} */
it("should set encryption properties by constructor", () => {
const encryption = new Encryption("clientPrivateKeySetLocation", "hyperwalletKeySetLocation",
"encryptionAlgorithm", "signAlgorithm", "encryptionMethod", 12);
encryption.clientPrivateKeySetLocation.should.be.equal("clientPrivateKeySetLocation");
encryption.hyperwalletKeySetLocation.should.be.equal("hyperwalletKeySetLocation");
encryption.encryptionAlgorithm.should.be.equal("encryptionAlgorithm");
encryption.signAlgorithm.should.be.equal("signAlgorithm");
encryption.encryptionMethod.should.be.equal("encryptionMethod");
encryption.jwsExpirationMinutes.should.be.equal(12);
});
});
/** @test {Encryption#encrypt} */
describe("encrypt()", () => {
let encryption;
let testMessage;
let clientPath;
let hwPath;
beforeEach(() => {
clientPath = path.join(__dirname, "..", "resources", "private-jwkset1");
hwPath = path.join(__dirname, "..", "resources", "public-jwkset1");
encryption = new Encryption(clientPath, hwPath);
testMessage = {
message: "Test message",
};
});
/** @test {Encryption#encrypt} */
it("should successfully encrypt and decrypt text message", (cb) => {
encryption.encrypt(testMessage).then((encryptedBody) => {
encryption.decrypt(encryptedBody).then((decryptedBody) => {
decryptedBody.payload.toString("utf8").should.be.deep.equal(JSON.stringify(testMessage));
cb();
});
});
});
/** @test {Encryption#encrypt} */
it("should successfully decode and encode encrypted text message", (cb) => {
encryption.encrypt(testMessage).then((encryptedBody) => {
const decodedMessage = Encryption.base64Decode(encryptedBody);
const encodedMessage = Encryption.base64Encode(decodedMessage);
encodedMessage.should.be.deep.equal(encryptedBody);
cb();
});
});
/** @test {Encryption#encrypt} */
it("should throw exception when wrong jwk key set location is given", (cb) => {
encryption = new Encryption("wrong_keyset_path", hwPath);
encryption.encrypt(testMessage)
.catch((error) => {
error.message.should.be.equal("Wrong JWK set location path = wrong_keyset_path");
cb();
});
});
/** @test {Encryption#encrypt} */
it("should throw exception when wrong jwk key is set for encryption", (cb) => {
encryption = new Encryption(clientPath, hwPath, "RS256");
encryption.encrypt(testMessage)
.catch((error) => {
error.message.should.be.equal("Failed to encrypt payload with key id = 2018_sig_rsa_RS256_2048");
cb();
});
});
/** @test {Encryption#encrypt} */
it("should throw exception when signing body with key algorithm that doesn't present in jwkset", (cb) => {
encryption = new Encryption(clientPath, hwPath, "RSA-OAEP-256", "RS256-not-present");
encryption.encrypt(testMessage)
.catch((error) => {
error.message.should.be.equal("JWK set doesn't contain key with algorithm = RS256-not-present");
cb();
});
});
/** @test {Encryption#encrypt} */
it("should throw exception when signing body with wrong jwk key", (cb) => {
encryption = new Encryption(clientPath, hwPath, "RSA-OAEP-256", "RSA-OAEP-256");
encryption.encrypt(testMessage)
.catch((error) => {
error.message.should.be.equal("Failed to sign with key id = 2018_enc_rsa_RSA-OAEP-256");
cb();
});
});
/** @test {Encryption#encrypt} */
it("should throw exception when jwk keyset file is invalid", (cb) => {
encryption = new Encryption(path.join(__dirname, "..", "resources", "jwkset-invalid"), hwPath);
encryption.encrypt(testMessage)
.catch((error) => {
error.message.should.be.equal("Failed to create keyStore from given jwkSet");
cb();
});
});
/** @test {Encryption#encrypt} */
it("should throw exception when jwk keyset file location is wrong", (cb) => {
encryption = new Encryption(path.join(__dirname, "..", "resources"), hwPath);
encryption.encrypt(testMessage)
.catch((error) => {
error.message.should.be.equal("Error: EISDIR: illegal operation on a directory, read");
cb();
});
});
/** @test {Encryption#encrypt} */
it("should successfully encrypt and decrypt text message with url keyset path", (cb) => {
fs.readFile(clientPath, { encoding: "utf-8" }, (err, keySetData) => {
nock("https://test-server")
.get("/test")
.reply(200, keySetData)
.get("/test")
.reply(200, keySetData);
encryption = new Encryption("https://test-server/test", hwPath);
const encryption2 = new Encryption(clientPath, hwPath);
encryption.encrypt(testMessage).then((encryptedBody) => {
encryption2.decrypt(encryptedBody).then((decryptedBody) => {
decryptedBody.payload.toString("utf8").should.be.deep.equal(JSON.stringify(testMessage));
cb();
});
});
});
});
/** @test {Encryption#encrypt} */
it("should throw exception when not supported encryption algorithm is given", (cb) => {
encryption = new Encryption(clientPath, hwPath, "unsupported_encryption_algorithm");
encryption.encrypt(testMessage)
.catch((error) => {
error.message.should.be.equal("JWK set doesn't contain key with algorithm = unsupported_encryption_algorithm");
cb();
});
});
});
/** @test {Encryption#decrypt} */
describe("decrypt()", () => {
let encryption;
let testMessage;
let clientPath;
let clientPath2;
let hwPath;
let hwPath2;
beforeEach(() => {
clientPath = path.join(__dirname, "..", "resources", "private-jwkset1");
hwPath = path.join(__dirname, "..", "resources", "public-jwkset1");
clientPath2 = path.join(__dirname, "..", "resources", "private-jwkset2");
hwPath2 = path.join(__dirname, "..", "resources", "public-jwkset2");
encryption = new Encryption(clientPath, hwPath);
testMessage = {
message: "Test message",
};
});
/** @test {Encryption#decrypt} */
it("should fail decryption when wrong private key is used", (cb) => {
const encryption2 = new Encryption(clientPath2, hwPath2);
encryption.encrypt(testMessage).then((encryptedBody) => {
encryption2.decrypt(encryptedBody)
.catch((error) => {
error.message.should.be.equal("Failed to decrypt payload with key id = 2018_enc_rsa_RSA-OAEP-256");
cb();
});
});
});
/** @test {Encryption#decrypt} */
it("should fail decryption when sign algorithm is not found in keyset", (cb) => {
const encryption2 = new Encryption(clientPath, hwPath, "RSA-OAEP-256", "RS256-OAEP-256");
encryption.encrypt(testMessage).then((encryptedBody) => {
encryption2.decrypt(encryptedBody)
.catch((error) => {
error.message.should.be.equal("JWK set doesn't contain key with algorithm = RS256-OAEP-256");
cb();
});
});
});
/** @test {Encryption#decrypt} */
it("should fail decryption when algorithm is not found in jwkset", (cb) => {
const encryption2 = new Encryption(clientPath, hwPath, "RSA-OAEP-256-absent");
encryption.encrypt(testMessage).then((encryptedBody) => {
encryption2.decrypt(encryptedBody)
.catch((error) => {
error.message.should.be.equal("JWK set doesn't contain key with algorithm = RSA-OAEP-256-absent");
cb();
});
});
});
/** @test {Encryption#decrypt} */
it("should fail signature verification when wrong public key is used", (cb) => {
const encryption2 = new Encryption(clientPath, hwPath2);
encryption.encrypt(testMessage).then((encryptedBody) => {
encryption2.decrypt(encryptedBody)
.catch((error) => {
error.message.should.be.equal("Failed to verify signature with key id = 2018_sig_rsa_RS256_2048");
cb();
});
});
});
/** @test {Encryption#decrypt} */
it("should throw exception when jws signature has expired", (cb) => {
const encryption2 = new Encryption(clientPath, hwPath2, "RSA-OAEP-256", "RS256", "A256CBC-HS512", -5);
encryption2.encrypt(testMessage).then(() => {
encryption2.signBody(testMessage).then((signedBody) => {
encryption2.checkSignature(signedBody)
.catch((error) => {
error.message.should.be.equal("JWS signature has expired");
cb();
});
});
});
});
});
});