EMV Generate/Verify MAC: add padding method selector (default Method 2)
This commit is contained in:
parent
0d08681d55
commit
9015ea9f40
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user