
Two-factor authentication with a bank card
2FA is pretty common on websites and API services these days. In fact it’s pretty much expected on any website the handles financial transaction or personal information.
Typically websites encourage their customers to register a mobile phone number with which to receive 2FA codes, or perhaps offer integration with authenticator products like Google Authenticator or Microsoft Authenticator. Unfortunately most customers just don’t bother to configure 2FA. In 2018 it was reported that less than 10% of active Google customers configured 2FA. Furthermore, 2FA is very often compromised if access to a customer’s mobile phone is gained.
Fortunately all retail bank customers typically already have a 2FA alternative; their ATM card and PIN. The bank holds information about the card that only the bank and the customer should know. Websites that accept payment card details are required to be PCI compliant. PCI (Payment Card Industry) compliance is not a simple thing - check out the many standards documents.
Using a payment card for 2FA
There are a few properties of a payment card that a card holder would know. Some details are more widely known than others:
- The 16 digit PAN - Visible on the card; payment receipts often include the first four and last four digits of the PAN
- The expiry date - Visible on the card; payment receipts often include the expiry date
- The CVC - Visible on the card
- The PIN number - this is only known to the customer
For 2FA we need a property that only the card holder would know - the PIN.
Capturing a payment card PIN
So how do you capture and validate a card PIN? You could capture the PIN on a website in clear text and send it to the web server to be validated, but this isn’t really that great. PIN numbers are meant to be encrypted on the terminal that they are entered into. Furthermore, you don’t want your load balancer, WAF or API accidentally logging a customer PIN number to a system log. The solution to this is to capture and encrypt the PIN in the website or mobile application and send the encrypted PIN, also known as a PIN block, to the server.
To verify that the PIN within a PIN block is correct you will typically need a special piece of equipment - a Hardware Security Module (HSM). Banks will have these as they are generally required for PCI compliance when performing card calculations for PIN and CVC codes. An HSM is a special type of hardware that is used to perform cryptographic operations and keep the secret data secret throughout the process. This is similar to the TPM in laptops and phones. You could perform the cryptographic operations in software, but there is less protection against the secret data being logged or memory dumped (remember OpenSSL heartbleed?).
Verifying a bank card PIN block
HSMs come in different flavours. For the process described here we need an HSM capable of banking operations such as a Thales Payshield.
Payment card PINs are encrypted with a format specified by ISO-9564 and use the Triple DES algorithm. Triple DES is a symmetric encryption algorithm. This isn’t great as we don’t want to send a clear key between the client and server.
The solution is to use an asymmetric encryption like RSA. This means we can encrypt the secret data with public key from the HSM on the client, and only send encrypted data back to the server. Remember the goal is that no clear-text data or keys are sent from the client device to the HSM.
So how would you encrypt a PIN block with and RSA key? The ISO-9564 specification clearly doesn’t support RSA encryption in such a small byte array. The solution is to generate a random triple DES key and use this to encrypt the PIN block using Format-1 (no card number). Then encrypt the random triple DES key with the RSA public key.
By today’s standards Triple DES is a weak and vulnerable form of encryption. It is vulnerable to brute force attacks. However is this use case the weakness is mitigated because we are creating a random key and only using it once.
Summary
We can now send both the Triple DES encrypted PIN block and the RSA encrypted Triple DES key to the web server and on to the HSM for verification thereby allowing the bank card PIN to be used as a second form of authorization on a banking website.
I will write another post with code samples on the steps described.