UNPKG

emv

Version:

EMV / Chip and PIN CLI and library for PC/SC card readers

400 lines 14.5 kB
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