EarnMorev1.0.0
types.tstypescript
/**
 * earnmore — Type definitions
 * Closed-loop reward card network library (PCI-DSS v4.0 / NIST / ISO compliant)
 */

/**
 * PBKDF2 iteration count meeting NIST SP 800-132 and PCI-DSS v4.0 requirements.
 * Use this value in production environments.
 */
export const PCI_DSS_PBKDF2_ITERATIONS = 310_000;

/**
 * NIST SP 800-57 Part 1 Rev.5 §5.3.6 recommended maximum cryptoperiod for
 * HMAC authentication keys: 2 years.
 */
export const NIST_HMAC_KEY_MAX_YEARS = 2;

export interface ComplianceConfig {
  /**
   * PBKDF2-SHA256 iteration count for CVV key derivation.
   * Default: `PCI_DSS_PBKDF2_ITERATIONS` (310,000) — satisfies NIST SP 800-132
   * and PCI-DSS Req 3.7.1.
   * Minimum enforced by the library: 1,000.
   *
   * Pass a lower value only in non-production contexts (e.g. unit tests).
   *
   * @see PCI_DSS_PBKDF2_ITERATIONS
   */
  pbkdf2Iterations?: number;

  /**
   * Key identifier for CVV generation/validation (must be a valid UUID).
   *
   * Per NIST SP 800-57 / ISO 11568-1, embedding a key ID in the authenticated
   * message enables key rotation without reissuing all cards. Store the keyId
   * alongside each card record and pass it back during CVV validation.
   */
  keyId: string;

  /**
   * Timestamp when this key/passkey was provisioned.
   * Used by `EarnmoreClient.isKeyExpired()` to enforce NIST SP 800-57 §5.3.6
   * cryptoperiod recommendations.
   * Defaults to the time the EarnmoreClient was constructed.
   */
  keyCreatedAt?: Date;

  /**
   * Audit event callback (CSF 2.0 DE.AE-02 / ISO 27001:2022 A.8.15).
   *
   * Called for every significant cryptographic operation. Events never contain
   * a full PAN, CVV, or raw key material — only masked PAN and metadata.
   * Feed these events into your SIEM, anomaly detection, or audit log.
   */
  onEvent?: (event: EarnmoreEvent) => void;
}

export type EarnmoreEventType =
  | "card_generated"
  | "cvv_valid"
  | "cvv_invalid"
  | "pan_encrypted"
  | "pan_decrypted"
  | "key_initialized"
  | "key_destroyed";

/**
 * Audit event emitted by the library for significant cryptographic operations.
 * Safe to log — never contains a full PAN, raw CVV, or key material.
 */
export interface EarnmoreEvent {
  /** Operation that triggered this event */
  type: EarnmoreEventType;

  /** UTC timestamp of the operation */
  timestamp: Date;

  /** Key identifier active at the time of the operation */
  keyId: string;

  /**
   * Masked PAN when the operation involved a specific card.
   * Format: first 6 (BIN) + "xxxxxx" + last 4.
   * Absent for key lifecycle events.
   */
  maskedPan?: string;
}

export type IdType = "passport" | "drivers_license" | "national_id";

export interface IdDocument {
  /** Type of identification document */
  type: IdType;

  /** The document number (alphanumeric) */
  number: string;
}

export interface CustomerAddress {
  line1: string;

  /** Optional second address line */
  line2?: string;
  city: string;
  country: string;
  zipCode: string;
}

export interface CustomerInfo {
  firstName: string;
  lastName: string;

  /** Pass an empty string if the customer has no middle name */
  middleName: string;

  /** Date of birth */
  birthday: Date;

  /**
   * Place of birth — letters and spaces only (e.g. "New York", "Manila").
   * The library will throw if this contains digits or special characters.
   */
  birthPlace: string;

  /** Primary identification document */
  idDocument: IdDocument;

  address: CustomerAddress;
}

/**
 * Full card issuance result.
 *
 * ⚠️ PCI-DSS Req 3.3.1: The `cvv` field is Sensitive Authentication Data (SAD)
 * and MUST NOT be stored after authorization. Use `redactCardResult()` to obtain
 * a storable representation that excludes the CVV.
 *
 * Store `keyId` alongside the card record — it is required for CVV validation
 * after a key rotation (NIST SP 800-57 / ISO 11568-1).
 */
export interface CardResult {
  /** 16-digit Primary Account Number */
  pan: string;

  /** Card expiry in MM/YY format */
  expiry: string;

  /**
   * 4-digit Card Verification Value.
   * SAD — do not persist this field after card issuance / authorization.
   */
  cvv: string;

  /**
   * Key identifier used to generate this CVV.
   * Persist this alongside the card record for future CVV validation.
   * Per NIST SP 800-57 / ISO 11568-1.
   */
  keyId: string;
}

/**
 * Cardholder Data (CHD) safe for storage after authorization.
 * The CVV/CVC is intentionally absent — per PCI-DSS Req 3.3.1,
 * SAD must not be retained once issuance/authorization is complete.
 */
export interface StorableCardData {
  /**
   * Masked PAN: first 6 digits (BIN) + 6 masked characters + last 4 digits.
   * Example: "001138xxxxxx1836"
   * Per PCI-DSS Req 3.5, full PANs must not be stored in plaintext.
   * Use encryptPAN() if the full PAN must be recoverable.
   */
  maskedPan: string;

  /** Card expiry in MM/YY format */
  expiry: string;

  /**
   * Key identifier — required for CVV validation after key rotation.
   * Per NIST SP 800-57 / ISO 11568-1.
   */
  keyId: string;
}

/**
 * AES-256-GCM encrypted PAN for compliant storage (PCI-DSS Req 3.4.1).
 * All fields are hex-encoded strings.
 */
export interface EncryptedPAN {
  /** 12-byte random IV (hex) — unique per encryption operation */
  iv: string;

  /** AES-256-GCM ciphertext (hex) */
  ciphertext: string;

  /** 16-byte GCM authentication tag (hex) — verifies integrity */
  authTag: string;
}

export const ALLOWED_PREFIXES = ["0011", "9212", "0513", "8989", "7312", "9876"] as const;

export type AllowedPrefix = (typeof ALLOWED_PREFIXES)[number];

export const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;

export function isValidUUID(uuid: string): boolean {
  return typeof uuid === "string" && UUID_REGEX.test(uuid);
}