2011-01-12

my feeble attempt at designing an NFC-based mobile phone payment system

I have a Nexus S (thanks, Mom!), which means I now have a phone that supports NFC. That has led me to think about how I would create a payment system that uses my mobile phone that is completely secure not only from sniffing but from phone theft as well. In other words something better than EMV (as recently shown again to be imperfect). A Buzz by Tim O'Reilly on the topic finally caused me to decide to blog about this.

Two things should be stated upfront. First, I am no security expert; these are just some ideas I have on the matter. Second, this is not meant to make sense to your parents; I'm assuming only people comfortable with a smartphone to use this system. I am more worried about a completely secure system than I am about my parents being able to use this approach.

So, to start, the payment app will require some passphrase protection. Whether this is a PIN, password, lock screen, or biometric I honestly don't care as long as it can be used to perform some encryption. The key point is that the user must know some secret to use the payment app properly. This protects the user from having their dropped or stolen phone being used to make a ton of charges.

Now, with the user logged into the payment app they visit their bank's website (I might be saying "bank", but I mean whomever is going to handle your transactions and knows your account number). There they will have a QR Code (should be a simple enough QR Code to not require a high-end camera feature on the phone) to scan with their payment app. The barcode will contain a very large account number (think 100 digits), an increment value, some identifier of who the bank is, and a protocol version number. Thanks to having a camera in the phone, the app just scans the QR Code and the user has to manually type nothing. And all the info is encrypted on the phone based on the passphrase as entered by the user. This prevents a clever thief from nabbing your phone and simply reading the memory to get the details necessary to clone your phone to make charges. The app will also reset its transaction count to zero.

With the payment app and phone ready to go, you can now pay for something. Let's say you are at In-N-Out and just bought some food for $6.54. You hold your phone up to the NFC pad of the POS system and two pieces of data are sent to your phone: name of the business and the amount to be paid. Your payment app will prompt you saying that In-N-Out would like you to pay them $6.54. This helps prevent random people trying to charge you under a fradulent name and the cost will be used as a way to verify with the bank how much you are authorizing to charge. If you decide to authorize the charge, you enter your passphrase and your details are decrypted and the payment app gets down to business.

First, it temporarily increments your transaction count, multiplies it against the increment value, appends it to your account number, and then hashes it. This creates a number identifying your account unique per transaction. It also allows the bank to easily associate the transaction with your account without any overally expensive process (hashes are relatively cheap). I am using a unique multiplier to help prevent people guessing the increment amount by having you do two transactions in a row at the same POS system (security-by-obscurity might be bad to rely on, but it doesn't hurt to make input values into a hash function harder to guess).

Next, the $6.54 cost has your account appended to it and that string is hashed. This will provide a way for the bank to easily verify that the amount being charged is what you want to be charged.

These two hashed values along with your bank's identifier are sent back to the POS system. All of this is sent to your bank along with the amount In-N-Out wants to charge you. The bank can easily look up your account by the hashed value. It can then easily hash the charge amount with your account to verify the amount be asked matches what you authorized. The bank clears the charge.

Back at the POS system, it tells your phone the charge went through and the transaction counter is permanently incremented by one. And the whole transaction is finished without any repeatable values being sent from your phone to the bank that can be sniffed. Nor is the charge amount unverifiable by the bank to prevent the shop from altering the charged amount without you knowing. And because of the hashing there is no easy way to brute force discover your account number.

I see two issues with all of this. One is that I used the account number for both hashed values. This could be a problem as it means the POS system has two separate hashes involving your account number, which could make it easier to reverse the hash (but very hard to do). While the value used in hashing the charge amount could be changed (e.g., the account + increment or something), I don't know how much that solves the problem (if it is a problem). Another solution is that the protocol version number can change what hash is used. This would allow, e.g., switching from SHA-2 to SHA-3 when it is selected.

The other is my worry that the transaction count could get out of sync. I see three possible solutions to this is. One is not to worry about it and rely on the phone waiting for the bank's response to increment the count. Two, the bank could assume one or two missed transaction increments (e.g., it was cancelled at the POS system) and simply check two or three possible identification values. Third, a back-channel communication from the payment app to the bank could be made to help keep the numbers in sync (something as simple as an HTTPS connection).

I think this covers phone theft (by encrypting the data on the phone), POS system lying about the charge amount (by creating a hash with the charge amount), sniffing (by hashing everything in a way that is unique per transaction with no repeated values as seen by the POS system) and replay attacks (by using a transaction counter in a hash).