EMV Generate/Verify MAC: add padding method selector (default Method 2)

This commit is contained in:
J8k3 2026-05-19 20:43:25 -04:00
parent 0d08681d55
commit 9015ea9f40
3 changed files with 14 additions and 10 deletions

View File

@ -12,16 +12,17 @@ import { generateIso9797Algorithm3Mac } from "./Iso9797.mjs";
* @param {string} messageHex
* @param {string} sessionKeyHex
* @param {number} outputBytes
* @param {string} paddingMethod
* @returns {Object}
*/
function generateEmvMac(messageHex, sessionKeyHex, outputBytes=8) {
function generateEmvMac(messageHex, sessionKeyHex, outputBytes=8, paddingMethod="Method 2") {
const normalizedKey = (sessionKeyHex || "").replace(/\s+/g, "");
if (!/^[0-9A-Fa-f]+$/.test(normalizedKey) || normalizedKey.length % 2 !== 0) {
throw new OperationError("Session key must be hex.");
}
return {
...generateIso9797Algorithm3Mac(messageHex, normalizedKey, "Method 2", outputBytes),
...generateIso9797Algorithm3Mac(messageHex, normalizedKey, paddingMethod, outputBytes),
algorithm: "EMV MAC"
};
}
@ -32,15 +33,16 @@ function generateEmvMac(messageHex, sessionKeyHex, outputBytes=8) {
* @param {string} messageHex
* @param {string} sessionKeyHex
* @param {string} expectedMac
* @param {string} paddingMethod
* @returns {Object}
*/
function verifyEmvMac(messageHex, sessionKeyHex, expectedMac) {
function verifyEmvMac(messageHex, sessionKeyHex, expectedMac, paddingMethod="Method 2") {
const normalizedExpected = (expectedMac || "").replace(/\s+/g, "").toUpperCase();
if (!/^[0-9A-F]+$/.test(normalizedExpected) || normalizedExpected.length % 2 !== 0) {
throw new OperationError("Expected MAC must be even-length hex.");
}
const generated = generateEmvMac(messageHex, sessionKeyHex, normalizedExpected.length / 2);
const generated = generateEmvMac(messageHex, sessionKeyHex, normalizedExpected.length / 2, paddingMethod);
return {
...generated,
expectedMacHex: normalizedExpected,

View File

@ -24,7 +24,7 @@ class GenerateEMVMAC extends Operation {
{
name: "EMV MAC sample",
input: "8424000008999E57FD0F47CACE0007",
args: ["0123456789ABCDEFFEDCBA9876543210", 8, false]
args: ["0123456789ABCDEFFEDCBA9876543210", "Method 2", 8, false]
}
];
this.infoURL = "https://en.wikipedia.org/wiki/EMV";
@ -32,6 +32,7 @@ class GenerateEMVMAC extends Operation {
this.outputType = "string";
this.args = [
{ name: "Session integrity key (hex)", type: "string", value: "", comment: "Provide the already-derived EMV integrity session key in hex. This op does not derive EMV keys for you." },
{ name: "Padding method", type: "option", value: ["Method 2", "Method 1"], comment: "Method 2 appends 0x80 then zero-pads to block boundary (ISO 7816-4; standard for EMV issuer scripts). Method 1 zero-pads to block boundary only." },
{ name: "Output bytes", type: "number", value: 8, min: 1, max: 8, comment: "Number of leftmost MAC bytes to return. EMV issuer scripts commonly use 8 bytes." },
{ name: "Output as JSON", type: "boolean", value: false, comment: "When enabled, returns the issuer-script input and full retail-MAC details." },
];
@ -43,8 +44,8 @@ class GenerateEMVMAC extends Operation {
* @returns {string}
*/
run(input, args) {
const [sessionKeyHex, outputBytes, outputJson] = args;
const result = generateEmvMac(input, sessionKeyHex, outputBytes);
const [sessionKeyHex, paddingMethod, outputBytes, outputJson] = args;
const result = generateEmvMac(input, sessionKeyHex, outputBytes, paddingMethod);
return outputJson ? JSON.stringify(result, null, 4) : result.macHex;
}
}

View File

@ -24,7 +24,7 @@ class VerifyEMVMAC extends Operation {
{
name: "EMV MAC verification sample",
input: "8424000008999E57FD0F47CACE0007",
args: ["0123456789ABCDEFFEDCBA9876543210", "22CB48394DFD1977", true]
args: ["0123456789ABCDEFFEDCBA9876543210", "22CB48394DFD1977", "Method 2", true]
}
];
this.infoURL = "https://en.wikipedia.org/wiki/EMV";
@ -33,6 +33,7 @@ class VerifyEMVMAC extends Operation {
this.args = [
{ name: "Session integrity key (hex)", type: "string", value: "", comment: "Provide the already-derived EMV integrity session key in hex. This op does not derive EMV keys for you." },
{ name: "Expected MAC (hex)", type: "string", value: "", comment: "Issuer-script MAC to compare against, expressed as even-length hex." },
{ name: "Padding method", type: "option", value: ["Method 2", "Method 1"], comment: "Must match the method used during generation. Method 2 appends 0x80 then zero-pads (ISO 7816-4; standard for EMV issuer scripts). Method 1 zero-pads only." },
{ name: "Output as JSON", type: "boolean", value: true, comment: "When enabled, returns the recomputed MAC and validity result." },
];
}
@ -43,8 +44,8 @@ class VerifyEMVMAC extends Operation {
* @returns {string}
*/
run(input, args) {
const [sessionKeyHex, expectedMac, outputJson] = args;
const result = verifyEmvMac(input, sessionKeyHex, expectedMac);
const [sessionKeyHex, expectedMac, paddingMethod, outputJson] = args;
const result = verifyEmvMac(input, sessionKeyHex, expectedMac, paddingMethod);
return outputJson ? JSON.stringify(result, null, 4) : String(result.valid);
}
}