Skip to content

Security: The-Pulse-Engine/pay-off

Security

docs/SECURITY.md

PAY-OFF Security Architecture

Non-Negotiable Security Requirements

PIN Handling

  • NEVER store UPI PIN, MPIN, or any authentication secret on device or server
  • PIN exists in memory ONLY during the single transaction execution
  • After use, PIN CharArray is immediately overwritten with zeros
  • 3 wrong PIN attempts → 24-hour lockout (matches UPI standard)

Cryptographic Keys

  • ALWAYS use Android Keystore (hardware-backed / StrongBox) for all cryptographic keys
  • Key algorithm: ECDSA P-256 (secp256r1/NIST P-256)
  • Key storage: TEE (Trusted Execution Environment) or StrongBox (dedicated secure element)
  • User authentication required for signing operations (biometric or device PIN)

Transaction Signing

  • ALWAYS sign every transaction payload with ECDSA P-256 before transmission
  • Canonical serialization: payeeVpa|amountPaise|timestamp|nonce(hex)
  • Hash: SHA-256 of canonical string
  • Signature: DER-encoded ECDSA signature

Anti-Replay Protection

  • ALWAYS include 8-byte cryptographically random nonce in every transaction
  • ALWAYS check nonce registry before processing any cryptogram
  • Nonce TTL: 24 hours (matching RBI offline payment window)
  • ALWAYS validate timestamp within 5-minute window

Data At Rest

  • All sensitive data encrypted with AES-256-GCM
  • Encryption key stored in Android Keystore (hardware-backed)
  • EncryptedSharedPreferences for configuration data
  • Room DB fields individually encrypted for payee data

Data In Transit

  • TLS 1.3 minimum for all network connections
  • Certificate pinning for NPCI and bank API endpoints
  • Binary SMS payload: signed with ECDSA before transmission

SIM Security

  • DETECT SIM swap: if MSISDN changes, force re-KYC before allowing any transaction
  • Store registered phone MSISDN in EncryptedSharedPreferences
  • Compare against current SIM on every app launch

Rate Limiting

  • Per-user: max 20 transactions/day (UPI standard)
  • Per-SIM: max 5 attempts/minute (anti-fraud)
  • Per-carrier: circuit breaker (5 failures → 2-minute cooldown)
  • Non-customer calls blocked during peak hours 9am-9pm IST (NPCI OC-215A)

Logging

  • NEVER log transaction amounts, VPAs, or user identifiers in plaintext
  • Debug builds: raw USSD/SMS responses stored in local DB only
  • Release builds: all sensitive fields redacted in logs

Cryptographic Operations

Operation Algorithm Key Size Storage
Transaction signing ECDSA P-256 256-bit Android Keystore (TEE/StrongBox)
Vault encryption AES-256-GCM 256-bit Android Keystore
PIN block encryption RSA-OAEP 2048-bit (bank key) Bank certificate (pinned)
Payload hashing SHA-256 256-bit N/A
Certificate pin SHA-256 256-bit Hardcoded in app

Binary SMS Payload Security

┌─────────────────────────────────────────────────┐
│ Binary SMS Payload (120 bytes / 140 byte limit) │
├──────────┬──────┬───────────────────────────────┤
│ Field    │ Size │ Security                       │
├──────────┼──────┼───────────────────────────────┤
│ Amount   │ 8B   │ Integrity via signature        │
│ VPA      │ 32B  │ Integrity via signature        │
│ Timestamp│ 8B   │ Anti-replay (5-min window)     │
│ Nonce    │ 8B   │ Anti-replay (24h uniqueness)   │
│ Signature│ 64B  │ ECDSA P-256 (R||S raw format)  │
└──────────┴──────┴───────────────────────────────┘

Threat Model

Threat Mitigation
PIN interception Hardware-backed input, never stored, zeroed after use
Man-in-the-middle TLS 1.3 + certificate pinning
Replay attack Nonce registry + timestamp window
SIM swap fraud MSISDN change detection + mandatory re-KYC
Brute force PIN 3 attempts → 24h lockout
Local data theft AES-256-GCM encryption, Keystore-backed keys
USSD interception Transaction-level ECDSA signatures
DDoS on UPI switch Rate limiting per OC-215A

There aren’t any published security advisories