Add Futurex Excrypt command parser

This commit is contained in:
J8k3 2026-05-16 10:38:09 -04:00
parent 5cc38496bd
commit 17c004a66b
4 changed files with 288 additions and 3 deletions

View File

@ -200,16 +200,25 @@ Important assumptions:
- `Derive DUKPT Key` is TDES DUKPT, not AES DUKPT - `Derive DUKPT Key` is TDES DUKPT, not AES DUKPT
- `Generate AS2805 KEK Validation` is an emulation-oriented helper and explicitly documents its simplifications in the operation comments - `Generate AS2805 KEK Validation` is an emulation-oriented helper and explicitly documents its simplifications in the operation comments
## 10) Key Container Inspection ## 10) Key Container And HSM Command Inspection
Operations: Operations:
- `Parse Thales payShield command`
- `Parse Futurex Excrypt command`
- `Parse TR-31 key block` - `Parse TR-31 key block`
- `Parse TR-34 B9 envelope` - `Parse TR-34 B9 envelope`
Use this when: Use this when:
- you need to inspect inbound wrapped-key material or transport frames during testing - you need to inspect vendor HSM command syntax, wrapped-key material, or transport frames during testing
Input: Input:
- full TR-31 or TR-34 payload as text or hex, depending on the operation comment - `Parse Thales payShield command`: raw legacy host command or response text
- `Parse Futurex Excrypt command`: raw bracketed Excrypt command or response text
- `Parse TR-31 key block` / `Parse TR-34 B9 envelope`: full payload as text or hex, depending on the operation comment
Important assumptions:
- the Thales and Futurex parsers currently focus on visible message syntax, delimiters, command identification, and field splitting rather than deep per-command semantic decoding
- `Parse Thales payShield command` expects the configured message-header length to be supplied in the op args
- `Parse Futurex Excrypt command` treats Excrypt messages as delimiter-based tag/value fields and commonly uses the `AO` field as the command code
## Chaining Patterns ## Chaining Patterns
@ -309,3 +318,13 @@ Operations:
Flow: Flow:
- inspect the KEK with `Calculate Payment KCV` - inspect the KEK with `Calculate Payment KCV`
- generate request or response RandomKeySend / RandomKeyReceive values with the AS2805 helper - generate request or response RandomKeySend / RandomKeyReceive values with the AS2805 helper
## J) Vendor Command Triage
Operations:
- `Parse Thales payShield command`
- `Parse Futurex Excrypt command`
Flow:
- paste the raw host message first before trying to interpret the business meaning
- use the parsed command code, delimiters, header, trailer, or tag/value split to confirm what family of command you are looking at
- follow with lower-level payment, EMV, PIN, or key-container recipes only after the transport syntax is understood

View File

@ -615,6 +615,7 @@
"Calculate Payment KCV", "Calculate Payment KCV",
"Generate AS2805 KEK Validation", "Generate AS2805 KEK Validation",
"Parse Thales payShield command", "Parse Thales payShield command",
"Parse Futurex Excrypt command",
"Parse TR-31 key block", "Parse TR-31 key block",
"Parse TR-34 B9 envelope", "Parse TR-34 B9 envelope",
"HMAC", "HMAC",

View File

@ -0,0 +1,177 @@
/**
* @license Apache-2.0
* @author Jacob Marks [https://jacobmarks.com]
*/
import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";
const COMMANDS = {
CAAV: "Calculate Account holder Authentication Value",
DAPT: "Decrypt Apple Pay Token",
DCDK: "Decrypt Cardholder Data Using DUKPT",
DGPT: "Decrypt Google Pay Token",
DRKI: "Identification Request",
DRKK: "Key Request",
DRKV: "Key Verification Request",
DSPT: "Decrypt Samsung Pay Token",
ECDK: "Encrypt Cardholder Data Using DUKPT",
EMPT: "Translate PIN Block for EMV Personalization",
EMVA: "Verify ARQC and optionally generate ARPC",
EMVG: "Generate Master Key",
EMVK: "Derive Key from Vendor Master Key and Derivation Data",
EMVM: "Generate or Verify MAC",
EMVP: "PIN Change",
EMVR: "EMV RSA Private Key or Component Translation to Encryption Under a Personalization Key",
EMVS: "Translate an ICC Master Key to Encryption Under a Personalization Key",
EMVT: "EMV Translate Sensitive Data",
GCAV: "Generate CAVV",
GCIV: "Generate a CVC3 IV",
GCSC: "Generate American Express CSC Value",
GCVC: "Generate CVC and CVC2",
GCVV: "Generate CVV or CVC Value",
GDAC: "Generate a Data Authentication Code",
GDCV: "Generate dCVV/CVC3",
GDDC: "Generate Discover dynamic CVV",
GEMC: "Generate EMV ICC Certificate",
GEMQ: "Generate EMV Issuer CSR",
GHMC: "Generate HCE Mobile Cryptogram",
GHMD: "Generate HCE Magstripe Verification Value",
GHMK: "Generate HCE Mobile Keys",
GHPB: "Generate HMAC and PBKDF2 Obfuscated Value",
GIDN: "Generate an ICC dynamic number",
GMAC: "Generate Message Authentication Code",
GNOF: "Generate New Offset",
GOFC: "Generate Offset of Clear PIN",
GOFF: "Generate PIN offset value",
GOPC: "Generate Offset and EMV PIN Change",
GPIN: "Generate PIN",
GPMC: "General Purpose Symmetric MAC",
GVDC: "Generate dynamic CVV",
HMAC: "Generate MAC Hash",
OFPC: "Perform EMV PIN Change Using Offset",
ONGQ: "Translate PAN Encrypted Under an Asymmetric Key Pair to a Different Trusted Public Key",
PEDK: "Key Request",
RKHM: "Generate or Verify HMAC",
RPIN: "PIN Change and Optional PIN Verification",
SSAD: "Sign Static Authentication Data with Issuer Private Key",
TCDK: "Translate Cardholder Data Using DUKPT",
TDKD: "Translate Cardholder Data Using DUKPT and Symmetric Keys",
TKDR: "Translate DUKPT Data to RSA with Specific Output Data",
TPCP: "Translate Encrypted PIN Coordinates to a PEK for Generate New Map Collection",
TPDD: "Allow an encrypted ANSI PIN block to be translated",
TPIN: "Translate PIN blocks",
TRPN: "Translate PIN from RSA to Symmetric PIN Block",
TSPN: "Translate PIN from PIN block to RSA encryption",
VAAV: "Verify Account Holder Authentication Value",
VCAC: "Verify EMV Mastercard CAP Token",
VCAV: "Verify Cardholder Authentication Verification Value",
VCSC: "Verify American Express CSC Value",
VCVC: "Verify CVC and CVC2",
VCVV: "Verify CVV",
VDAC: "Verify a Data Authentication Code",
VDCV: "Verify CVC3",
VDDC: "Verify dynamic CVC value",
VEMI: "Verify an EMV Issuer Certificate",
VHMC: "Verify HCE Mobile Cryptogram",
VHMD: "Verify HCE Magstripe Verification Value",
VIDN: "Verify an ICC dynamic number",
VMAC: "Verify Message Authentication Code",
VMAP: "Verify MAC and PIN",
VPIN: "Verify PIN",
VVDC: "Verify a dynamic CVV",
WPIN: "Weak PIN checking",
XPIN: "PIN translation"
};
/**
* Parses an Excrypt field into tag/value components.
*
* @param {string} field
* @returns {{raw: string, tag: string, value: string}}
*/
function parseField(field) {
const tag = field.substring(0, Math.min(2, field.length)).toUpperCase();
return {
raw: field,
tag,
value: field.substring(tag.length)
};
}
/**
* Parse Futurex Excrypt command operation.
*/
class ParseFuturexExcryptCommand extends Operation {
/**
* ParseFuturexExcryptCommand constructor
*/
constructor() {
super();
this.name = "Parse Futurex Excrypt command";
this.module = "Payment";
this.description = "Paste a Futurex Excrypt command or response into the input field as text.<br><br><b>General syntax:</b> Excrypt messages are enclosed by opening and closing delimiters, typically <code>[</code> and <code>]</code>. Inside the message, fields are semicolon-delimited. Each field is a tag/value pair, for example <code>AOECHO</code> where <code>AO</code> is the tag and <code>ECHO</code> is the value. The command code is commonly carried in the <code>AO</code> field.<br><br><b>Input:</b> raw Excrypt message text.<br><br>This operation parses the visible Excrypt message syntax, extracts semicolon-delimited fields, splits fields into tag/value pairs, and resolves the <code>AO</code> command code to a known payment command name when available from the Futurex payment integration guide.";
this.inlineHelp = "<strong>Syntax:</strong> <code>[tagvalue;tagvalue;...]</code> where fields are separated by semicolons and tags are typically two characters such as <code>AO</code>.<br><strong>Input:</strong> raw Futurex Excrypt message text.";
this.testDataSamples = [
{
name: "Excrypt command sample",
input: "[AOGMAC;FS6;RV0011223344556677;]"
}
];
this.inputType = "string";
this.outputType = "string";
this.args = [];
}
/**
* @param {string} input
* @returns {string}
*/
run(input) {
const rawInput = (input || "").replace(/\r?\n/g, "");
if (!rawInput.length) {
throw new OperationError("No input.");
}
const openingDelimiterPresent = rawInput.startsWith("[");
const closingDelimiterPresent = rawInput.endsWith("]");
const body = rawInput.replace(/^\[/, "").replace(/\]$/, "");
const rawFields = body.split(";").filter(field => field.length > 0);
if (!rawFields.length) {
throw new OperationError("No Excrypt fields found.");
}
const fields = rawFields.map(parseField);
const commandField = fields.find(field => field.tag === "AO") || fields[0];
const commandCode = commandField.value.toUpperCase();
const commandName = COMMANDS[commandCode] || null;
const notes = [];
if (!openingDelimiterPresent || !closingDelimiterPresent) {
notes.push("Message is missing one or both expected Excrypt outer delimiters.");
}
if (!commandName) {
notes.push("Command code was not found in the Futurex payment integration guide lookup.");
}
return JSON.stringify({
rawInput,
openingDelimiterPresent,
closingDelimiterPresent,
body,
rawFields,
fields,
commandFieldTag: commandField.tag,
commandCode,
commandName,
fieldCount: fields.length,
notes
}, null, 4);
}
}
export default ParseFuturexExcryptCommand;

View File

@ -92,6 +92,94 @@ TestRegister.addTests([
} }
] ]
}, },
{
name: "Parse Futurex Excrypt command: bracketed fields",
input: "[AOGMAC;FS6;RV0011223344556677;]",
expectedOutput: JSON.stringify({
rawInput: "[AOGMAC;FS6;RV0011223344556677;]",
openingDelimiterPresent: true,
closingDelimiterPresent: true,
body: "AOGMAC;FS6;RV0011223344556677;",
rawFields: [
"AOGMAC",
"FS6",
"RV0011223344556677"
],
fields: [
{
raw: "AOGMAC",
tag: "AO",
value: "GMAC"
},
{
raw: "FS6",
tag: "FS",
value: "6"
},
{
raw: "RV0011223344556677",
tag: "RV",
value: "0011223344556677"
}
],
commandFieldTag: "AO",
commandCode: "GMAC",
commandName: "Generate Message Authentication Code",
fieldCount: 3,
notes: []
}, null, 4),
recipeConfig: [
{
op: "Parse Futurex Excrypt command",
args: []
}
]
},
{
name: "Parse Futurex Excrypt command: missing closing delimiter",
input: "[AOVMAC;FS6;RV89ABCDEF",
expectedOutput: JSON.stringify({
rawInput: "[AOVMAC;FS6;RV89ABCDEF",
openingDelimiterPresent: true,
closingDelimiterPresent: false,
body: "AOVMAC;FS6;RV89ABCDEF",
rawFields: [
"AOVMAC",
"FS6",
"RV89ABCDEF"
],
fields: [
{
raw: "AOVMAC",
tag: "AO",
value: "VMAC"
},
{
raw: "FS6",
tag: "FS",
value: "6"
},
{
raw: "RV89ABCDEF",
tag: "RV",
value: "89ABCDEF"
}
],
commandFieldTag: "AO",
commandCode: "VMAC",
commandName: "Verify Message Authentication Code",
fieldCount: 3,
notes: [
"Message is missing one or both expected Excrypt outer delimiters."
]
}, null, 4),
recipeConfig: [
{
op: "Parse Futurex Excrypt command",
args: []
}
]
},
{ {
name: "Parse TR-31 key block: fixed header only", name: "Parse TR-31 key block: fixed header only",
input: "D0016D0AB00E0000", input: "D0016D0AB00E0000",