@adyen/api-library
Version:
The Adyen API Library for NodeJS enables you to work with Adyen APIs.
116 lines • 5.89 kB
JavaScript
;
/*
* ######
* ######
* ############ ####( ###### #####. ###### ############ ############
* ############# #####( ###### #####. ###### ############# #############
* ###### #####( ###### #####. ###### ##### ###### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ##### ######
* ###### ###### #####( ###### #####. ###### ##### ##### ######
* ############# ############# ############# ############# ##### ######
* ############ ############ ############# ############ ##### ######
* ######
* #############
* ############
* Adyen NodeJS API Library
* Copyright (c) 2026 Adyen B.V.
* This file is open source and available under the MIT license.
* See the LICENSE file for more info.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.NexoSecurityManager = void 0;
const nexoSecurityException_1 = require("./nexoSecurityException");
const nexoDerivedKeyGenerator_1 = require("./nexoDerivedKeyGenerator");
const nexoCryptoPrimitives_1 = require("./nexoCryptoPrimitives");
/**
* Handles encryption, decryption, and integrity validation for Cloud Device API
* SaleToPOI messages using AES-256-CBC and HMAC-SHA256.
*/
class NexoSecurityManager {
constructor(credentials) {
this.derivedKey = null;
validateCredentials(credentials);
this.credentials = credentials;
}
/**
* Encrypts the SaleToPOI message using the provided message header and credential details.
*/
encrypt(saleToPoiMessageJson, messageHeader) {
try {
const dk = this.getDerivedKey();
const messageBytes = Buffer.from(saleToPoiMessageJson, "utf-8");
const ivNonce = (0, nexoCryptoPrimitives_1.generateRandomIvNonce)();
const encryptedBytes = (0, nexoCryptoPrimitives_1.crypt)(messageBytes, dk, ivNonce, "encrypt");
const hmacBytes = (0, nexoCryptoPrimitives_1.hmac)(messageBytes, dk);
return {
MessageHeader: messageHeader,
NexoBlob: encryptedBytes.toString("base64"),
SecurityTrailer: {
AdyenCryptoVersion: this.credentials.adyenCryptoVersion,
KeyIdentifier: this.credentials.keyIdentifier,
KeyVersion: this.credentials.keyVersion,
Nonce: ivNonce.toString("base64"),
Hmac: hmacBytes.toString("base64"),
},
};
}
catch (e) {
if (e instanceof nexoSecurityException_1.NexoSecurityException)
throw e;
throw new nexoSecurityException_1.NexoSecurityException("Cannot encrypt the SaleToPOISecuredMessage", e);
}
}
/**
* Decrypts a SaleToPOI secured message and returns the plaintext JSON string.
*/
decrypt(secured) {
let decryptedBytes;
try {
const dk = this.getDerivedKey();
const encryptedBytes = Buffer.from(secured.NexoBlob, "base64");
const ivNonce = Buffer.from(secured.SecurityTrailer.Nonce, "base64");
decryptedBytes = (0, nexoCryptoPrimitives_1.crypt)(encryptedBytes, dk, ivNonce, "decrypt");
const receivedHmac = Buffer.from(secured.SecurityTrailer.Hmac, "base64");
(0, nexoCryptoPrimitives_1.validateHmac)(receivedHmac, decryptedBytes, dk);
}
catch (_a) {
// Emit a single, generic error for every failure mode (padding, HMAC, nonce, decoding).
// Distinguishable errors would reintroduce a CBC padding-oracle side channel.
throw new nexoSecurityException_1.NexoSecurityException("Decryption of the SaleToPOISecuredMessage failed");
}
// Only reachable once the message is cryptographically verified, so a distinct diagnostic
// here is safe: a tampered message fails the HMAC check above and never reaches this point.
this.validateKeyMetadata(secured);
return decryptedBytes.toString("utf-8");
}
validateKeyMetadata(secured) {
const trailer = secured.SecurityTrailer;
if (trailer.KeyIdentifier !== this.credentials.keyIdentifier ||
trailer.KeyVersion !== this.credentials.keyVersion ||
trailer.AdyenCryptoVersion !== this.credentials.adyenCryptoVersion) {
// Do not include the KeyIdentifier value: it is credential material and this message
// is likely to end up in logs. Only report the non-sensitive version numbers.
throw new nexoSecurityException_1.NexoSecurityException("SecurityTrailer key metadata does not match the configured credentials: " +
`expected KeyVersion=${this.credentials.keyVersion}, AdyenCryptoVersion=${this.credentials.adyenCryptoVersion} ` +
`but received KeyVersion=${trailer.KeyVersion}, AdyenCryptoVersion=${trailer.AdyenCryptoVersion} ` +
`(KeyIdentifier ${trailer.KeyIdentifier === this.credentials.keyIdentifier ? "matches" : "differs"})`);
}
}
getDerivedKey() {
if (!this.derivedKey) {
this.derivedKey = (0, nexoDerivedKeyGenerator_1.deriveKeyMaterial)(this.credentials.passphrase);
}
return this.derivedKey;
}
}
exports.NexoSecurityManager = NexoSecurityManager;
function validateCredentials(credentials) {
if (!credentials ||
!credentials.passphrase ||
!credentials.keyIdentifier ||
!Number.isFinite(credentials.keyVersion) ||
!Number.isFinite(credentials.adyenCryptoVersion)) {
throw new nexoSecurityException_1.NexoSecurityException("Invalid Security Key");
}
}
//# sourceMappingURL=nexoSecurityManager.js.map