emv
Version:
EMV / Chip and PIN CLI and library for PC/SC card readers
400 lines • 14.5 kB
TypeScript
import type { CardResponse, SmartCard, Reader } from './types.js';
/**
* AFL (Application File Locator) entry
*/
export interface AflEntry {
sfi: number;
firstRecord: number;
lastRecord: number;
sdaRecords: number;
}
/**
* PDOL/CDOL entry (tag + length)
*/
export interface DolEntry {
tag: number;
length: number;
}
/**
* Transaction options
*/
export interface TransactionOptions {
/** Amount in minor units (e.g., cents) */
amount: number;
/** ISO 4217 currency code (e.g., 0x0840 for USD) */
currencyCode: number;
/** Transaction type (0x00 = purchase, 0x01 = cash, 0x09 = cashback) */
transactionType?: number;
/** Cryptogram type to request: 'ARQC' (online), 'TC' (offline approve), 'AAC' (decline) */
cryptogramType?: 'ARQC' | 'TC' | 'AAC';
/** Additional tag values for PDOL */
pdolValues?: Map<number, Buffer>;
/** Additional tag values for CDOL */
cdolValues?: Map<number, Buffer>;
}
/**
* Transaction result
*/
export interface TransactionResult {
success: boolean;
error?: string | undefined;
/** AIP from GPO response */
aip?: Buffer | undefined;
/** AFL entries from GPO response */
afl?: AflEntry[] | undefined;
/** Records read from card */
records?: Buffer[] | undefined;
/** Cryptogram type returned by card */
cryptogramType?: 'ARQC' | 'TC' | 'AAC' | undefined;
/** Application cryptogram */
cryptogram?: Buffer | undefined;
/** Application Transaction Counter */
atc?: number | undefined;
/** Full Generate AC response */
generateAcResponse?: Buffer | undefined;
}
/**
* Record read from card with metadata
*/
export interface RecordData {
/** Short File Identifier */
sfi: number;
/** Record number */
recordNumber: number;
/** Raw record data */
data: Buffer;
}
/**
* Options for building default PDOL data
*/
export interface PdolBuildOptions {
/** Amount in minor units (e.g., cents) */
amount: number;
/** ISO 4217 currency code (e.g., 0x0840 for USD) */
currencyCode: number;
/** Transaction type (default 0x00 = purchase) */
transactionType?: number;
/** Custom tag value overrides */
overrides?: Map<number, Buffer>;
}
/**
* Options for building default CDOL data
*/
export interface CdolBuildOptions {
/** Amount in minor units (e.g., cents) */
amount: number;
/** ISO 4217 currency code (e.g., 0x0840 for USD) */
currencyCode: number;
/** Transaction type (default 0x00 = purchase) */
transactionType?: number;
/** Terminal Verification Results (default all zeros) */
tvr?: Buffer;
/** Custom tag value overrides */
overrides?: Map<number, Buffer>;
}
/**
* CVM (Cardholder Verification Method) method types
*/
export type CvmMethod = 'fail' | 'plaintext_pin_icc' | 'enciphered_pin_online' | 'plaintext_pin_icc_signature' | 'enciphered_pin_icc' | 'enciphered_pin_icc_signature' | 'signature' | 'no_cvm' | 'unknown';
/**
* CVM condition types
*/
export type CvmCondition = 'always' | 'unattended_cash' | 'not_unattended_cash_manual_pin' | 'terminal_supports_cvm' | 'manual_cash' | 'purchase_with_cashback' | 'amount_under_x' | 'amount_over_x' | 'amount_under_y' | 'amount_over_y' | 'unknown';
/**
* A single CVM rule
*/
export interface CvmRule {
/** The verification method */
method: CvmMethod;
/** The condition for this rule to apply */
condition: CvmCondition;
/** If true, transaction fails if this CVM fails. If false, try next rule. */
failIfUnsuccessful: boolean;
/** Raw CVM byte */
cvmByte: number;
/** Raw condition byte */
conditionByte: number;
}
/**
* Parsed CVM list
*/
export interface CvmList {
/** Amount X threshold (in currency minor units) */
amountX: number;
/** Amount Y threshold (in currency minor units) */
amountY: number;
/** CVM rules in priority order */
rules: CvmRule[];
}
/**
* Context for CVM evaluation
*/
export interface CvmContext {
/** Transaction amount in minor units */
amount?: number;
/** Whether terminal supports CVM */
terminalSupportsCvm?: boolean;
/** Whether this is unattended cash */
unattendedCash?: boolean;
/** Whether this is manual cash */
manualCash?: boolean;
/** Whether this is purchase with cashback */
purchaseWithCashback?: boolean;
}
/**
* Information about a discovered application from PSE/PPSE
*/
export interface DiscoveredApp {
/** Application Identifier (AID) as hex string */
aid: string;
/** Application label (human-readable name) */
label?: string | undefined;
/** Application priority indicator */
priority?: number | undefined;
}
/**
* Result from discovering applications via PSE/PPSE
*/
export interface DiscoverAppsResult {
/** Whether the PSE/PPSE selection succeeded */
success: boolean;
/** List of discovered applications */
apps: DiscoveredApp[];
/** SFI used for reading records */
sfi: number;
/** Raw PSE/PPSE response buffer */
pseBuffer?: Buffer | undefined;
}
/**
* Result from parsing a GPO (GET PROCESSING OPTIONS) response
*/
export interface GpoResult {
/** Application Interchange Profile */
aip: Buffer | undefined;
/** Application File Locator entries */
afl: AflEntry[];
}
/**
* Result from parsing a Generate AC response
*/
export interface GenerateAcResult {
/** Type of cryptogram returned */
cryptogramType: 'ARQC' | 'TC' | 'AAC' | undefined;
/** Application cryptogram value */
cryptogram: Buffer | undefined;
/** Application Transaction Counter */
atc: number | undefined;
}
/**
* Parse AFL (Application File Locator) from buffer.
* Each AFL entry is 4 bytes: SFI (5 bits) | 000, first record, last record, SDA records
*/
export declare function parseAfl(buffer: Buffer): AflEntry[];
/**
* Parse a GPO (GET PROCESSING OPTIONS) response buffer.
* Supports both Format 1 (tag 80) and Format 2 (tag 77) responses.
*
* @param buffer - Raw GPO response data (without status words)
* @returns Parsed AIP and AFL entries
*/
export declare function parseGpoResponseBuffer(buffer: Buffer): GpoResult;
/**
* Parse a Generate AC response buffer.
* Extracts cryptogram type, cryptogram value, and ATC.
*
* @param buffer - Raw Generate AC response data (without status words)
* @returns Parsed cryptogram details
*/
export declare function parseGenerateAcResponse(buffer: Buffer): GenerateAcResult;
/**
* Parse PDOL or CDOL (Data Object List) from buffer.
* Format: tag (1-2 bytes) + length (1 byte), repeated
*/
export declare function parsePdol(buffer: Buffer): DolEntry[];
/**
* Build PDOL/CDOL data from tag entries and values.
* Missing values are padded with zeros.
*/
export declare function buildPdolData(entries: DolEntry[], tagValues: Map<number, Buffer>): Buffer;
/**
* Build PDOL data with sensible default values for common EMV tags.
* Use this when you need to construct PDOL data for GPO but don't have
* all the specific terminal values.
*
* @param entries - PDOL entries from parsePdol()
* @param options - Transaction options (amount, currency, etc.)
* @returns Buffer containing PDOL data ready for GPO
*/
export declare function buildDefaultPdolData(entries: DolEntry[], options: PdolBuildOptions): Buffer;
/**
* Build standard CDOL data for Generate AC command.
* This constructs the commonly required CDOL1 data in the standard order.
*
* @param options - Transaction options (amount, currency, etc.)
* @returns Buffer containing CDOL data ready for Generate AC
*/
export declare function buildDefaultCdolData(options: CdolBuildOptions): Buffer;
/**
* Parse CVM (Cardholder Verification Method) List from EMV tag 8E.
* The CVM List contains amount thresholds and a list of verification rules.
*
* @param buffer - The CVM List data (tag 8E)
* @returns Parsed CVM list with amount thresholds and rules
*/
export declare function parseCvmList(buffer: Buffer): CvmList;
/**
* Evaluate CVM rules against a transaction context and return the first matching rule.
* This simulates the terminal's CVM selection process.
*
* @param cvmList - Parsed CVM list
* @param context - Transaction context with relevant conditions
* @returns The first matching CVM rule, or undefined if none match
*/
export declare function evaluateCvm(cvmList: CvmList, context: CvmContext): CvmRule | undefined;
/**
* Convert a 2-character decimal string to a single BCD byte.
*
* Each character becomes one nibble: e.g. "25" -> 0x25 (high nibble 2, low nibble 5).
*
* @param str - A 2-character string containing decimal digits ("0"-"9")
* @throws TypeError if input is not exactly 2 decimal digits
*/
export declare function stringToBcd(str: string): number;
/**
* EMV Application for interacting with chip cards via PC/SC readers.
*
* @example
* ```typescript
* import { Devices } from 'smartcard';
* import { EmvApplication, format } from 'emv';
*
* const devices = new Devices();
*
* devices.on('card-inserted', async ({ reader, card }) => {
* const emv = new EmvApplication(reader, card);
* const response = await emv.selectPse();
* console.log(format(response));
* });
*
* devices.start();
* ```
*/
export declare class EmvApplication {
#private;
constructor(reader: Reader, card: SmartCard);
/**
* Select the Payment System Environment (PSE) directory.
* This is typically the first command sent to a contact payment card.
*/
selectPse(): Promise<CardResponse>;
/**
* Select the Proximity Payment System Environment (PPSE) directory.
* This is the first command sent to a contactless payment card.
*/
selectPpse(): Promise<CardResponse>;
/**
* Select an EMV application by its AID.
* @param aid - Application Identifier (5-16 bytes)
*/
selectApplication(aid: Buffer | readonly number[]): Promise<CardResponse>;
/**
* Read a record from a Short File Identifier (SFI).
* @param sfi - Short File Identifier (1-30)
* @param record - Record number (0-255)
*/
readRecord(sfi: number, record: number): Promise<CardResponse>;
/**
* Read all records specified in the Application File Locator (AFL).
* This automates the process of reading all card data after GPO.
*
* @param afl - AFL entries (from parseAfl) or raw AFL buffer
* @returns Array of records with SFI and record number metadata
*/
readAllRecords(afl: AflEntry[] | Buffer): Promise<RecordData[]>;
/**
* Discover payment applications on the card via PSE (Payment System Environment).
* This reads the PSE directory and extracts information about available applications.
*
* @returns Result containing list of discovered applications
*/
discoverApplications(): Promise<DiscoverAppsResult>;
/**
* Verify the cardholder PIN (plaintext).
* @param pin - PIN code as a string of 4-12 digits
* @returns CardResponse with status words indicating success or failure:
* - SW 9000: PIN verified successfully
* - SW 63CX: Wrong PIN, X attempts remaining
* - SW 6983: PIN blocked (too many failed attempts)
* - SW 6984: PIN not initialized
*/
verifyPin(pin: string): Promise<CardResponse>;
/**
* Change the cardholder PIN (plaintext).
*
* **Note:** Most payment cards restrict PIN change to specific environments
* (ATM, bank terminal). This method is primarily useful for test cards.
*
* @param oldPin - Current PIN code as a string of 4-12 digits
* @param newPin - New PIN code as a string of 4-12 digits
* @returns CardResponse with status words indicating success or failure:
* - SW 9000: PIN changed successfully
* - SW 63CX: Wrong old PIN, X attempts remaining
* - SW 6983: PIN blocked (too many failed attempts)
* - SW 6984: PIN not initialized
*/
changePin(oldPin: string, newPin: string): Promise<CardResponse>;
/**
* Get data element from the card by tag.
* @param tag - EMV tag (1-2 bytes, e.g., 0x9F17 for PIN Try Counter)
* @returns CardResponse with the requested data or error status:
* - SW 9000: Success, data returned
* - SW 6A88: Referenced data not found
*/
getData(tag: number): Promise<CardResponse>;
/**
* Initiate transaction processing (GET PROCESSING OPTIONS).
* @param pdolData - Optional PDOL (Processing Data Object List) values
* @returns CardResponse containing AIP and AFL on success:
* - SW 9000: Success, AIP and AFL returned
* - SW 6985: Conditions of use not satisfied
*/
getProcessingOptions(pdolData?: Buffer | readonly number[]): Promise<CardResponse>;
/**
* Generate an Application Cryptogram for transaction authorization.
* @param cryptogramType - Type of cryptogram to generate:
* - 0x00: AAC (Application Authentication Cryptogram) - decline
* - 0x40: TC (Transaction Certificate) - approve offline
* - 0x80: ARQC (Authorization Request Cryptogram) - go online
* @param cdolData - CDOL (Card Data Object List) data
* @returns CardResponse containing the cryptogram on success
*/
generateAc(cryptogramType: number, cdolData: Buffer | readonly number[]): Promise<CardResponse>;
/**
* Perform internal authentication for Dynamic Data Authentication (DDA).
* @param authData - Authentication data (typically unpredictable number from terminal)
* @returns CardResponse containing signed dynamic application data
*/
internalAuthenticate(authData: Buffer | readonly number[]): Promise<CardResponse>;
/**
* Get the card's ATR (Answer To Reset)
*/
getAtr(): string;
/**
* Get the reader name
*/
getReaderName(): string;
/**
* Perform a complete EMV transaction flow.
* This orchestrates: GPO → Read Records → Generate AC
*
* @param options - Transaction options including amount and currency
* @returns TransactionResult with cryptogram and card data
*/
performTransaction(options: TransactionOptions): Promise<TransactionResult>;
}
/**
* Factory function to create an EmvApplication instance
*/
export declare function createEmvApplication(reader: Reader, card: SmartCard): EmvApplication;
export default createEmvApplication;
//# sourceMappingURL=emv-application.d.ts.map