We have to implement a way to securely transfer data from a POS to a mobile app using QR codes.

  • The mobile app gets the payment parameters (amount, description, purchase_id, store_id) to create the payment intent by scanning a QR code generated from the POS,
  • the app passes the info to a trusted server through an API and
  • if the payment succeeds, it generates a QR code for the POS, including some parameters the store needs to match the payment back in the POS system.

We need to make sure that the system is secure so is not possible for an attacker to fake these QR codes.

Would this ensure the system security?

  1. The trusted server generates a key pair and a shared secret
  2. The commerce (POS) generates a key pair
  3. The trusted server and the POS exchange their public keys and the shared secret
  4. The QR code contains 2 parts: an encrypted body + signature
  5. The encrypted body is a JSON string encrypted using the server’s / POS’s public key. The only way to decipher the body is using the private key so the structure of the message remains unknown.
  6. The signature is a HMAC-SHA256(body, shared-key). So we can verify the integrity of the body once decrypted in the server or POS.

Does this make sense?

  • $\begingroup$ To clarify: 1.) Can we assume the POS/Server connection and maybe even the App/Server connection to be secured (think TLS, SSL)? 2.) Are we talking about multiple stores with potentially multiple POSs per store, or (very unlikely) only a single store with a single POS? 3.) What exactly is your scenario or reason to put part of the POS payment process into customer hands via a mobile app? After all, this opens up multiple attack vectors and even minor annoyances like a connection failure between customer app and server might result in a non-sale… which is the last thing shops want. $\endgroup$
    – e-sushi
    Oct 6, 2017 at 3:06
  • $\begingroup$ In my answer below, I have ONLY considered the attack vector "Customer creates a fake response QR to get products for a lower price than intended". If you want a answer that considers more attack vectors, you must include them in your original question. $\endgroup$ Oct 6, 2017 at 5:03
  • $\begingroup$ Try to better define "securely". What's the objective of an adversary? Is that to forge fake messages, to modify genuine messages, to understand genuine messages? What's trusted, and how does it gets and protects its keys? $\endgroup$
    – fgrieu
    Oct 6, 2017 at 6:23

1 Answer 1


If the POS system is online, then its simple. Just have a purchase ID in the QR. The POS will call your API, and "create" an unpaid purchase. This purchase ID is encoded into an QR.

Then the customer scans this QR, and approves payment. You don't even need to display a returning QR, because the POS could poll the API at regular intervals and check when the ID is "paid".

If you want to be able to have an offline POS, its also pretty easy. You need a shared key, that is unique per physical store location (store_id must also be unique per store location, store_id is then used by the payment server to look up the correct HMAC key).

If a store accidentially leaks their shared key, they must replace it immediately, else they risk customers creating fake QR codes to get products for free.

The store have a QR code with: amount + "-" + description + "-" + purchase_id + "-" + store_id + hmac_sha256(amount + "-" + description + "-" + purchase_id + "-" + store_id + "-unpaid-", SHARED_KEY)

You send this to server. Server validates the hmac, and then, upon payment, creates the following QR: hmac_sha256(amount + "-" + description + "-" + purchase_id + "-" + store_id + "-paid-", SHARED_KEY)

Assuming a shared hmac_sha256 key of: SHAREDKESHAREDKESHAREDKESHAREDKE

First you create the purchase: 100-USB memory-0001-1212-475d1fa90f24e134e76ffcc075ad3e7ce2c76e1ae78c7c8408c7508882ee9952 (89 characters)

Then app sends this for payment. When payment is done, you create the following QR: 0899dd28bb8e5cfe6d54deb0000df01cec88f65b85a0b4148d7b49dddd2453d9 (64 characters)

NOTE: The original purchase data is NOT needed in the "reply QR" - the cash register already have this data from the pending transaction, that it can use to check the reply QR. So the "reply QR" becomes much smaller and more easier to scan.

NOTE2: Even if someone learns the structure, its still a impossibility to create a valid reply HMAC from the request HMAC. Changing "-unpaid-" into "-paid-" would require knowledge of the shared key.

NOTE3: purchase_id must be unique among a single physical store location (store_id). If a store is generating duplicate purchase_id's, a customer can reuse a paid order to get products for free. If a store location has multiple offline POS not even connected with each other, the store, which also chooses purchase_id, must prepent the POS location to purchase_id, eg have purchase_id 10000-19999 for POS 1, 20000-29999 for POS 2, and so on. In my solution, purchase_id can be of any length required for the purchase_id not to roll over.

You really don't need to mix in RSA at all.

Actually, you don't need an HMAC for the request at all (but its ALWAYS required for the Response). Its just a customer protection so any scan errors or any sheninigans doesn't cause a customer to do a payment for a non-existing purchase. The reason is that the request isn't security-sensitive, is because if the customer edits the purchase request, the response QR for the paid purchase will not validate, and the hacking customer will not get anything out of it.

But you must atleast have some error protection so not a damaged QR display or strong light falling on QR-code or the phone camera causes it to generate a purchase request, which when paid for, will not release any products, and thus cause a legit, fair customer to lose money. Thats extremely unlikely however, as QR code contains their own checksums, but feel free to add any checksum you like to the request. It can be HMAC, a md5 hash, a CRC32, whatever. Even a truncated hash suffices, if you must keep the request size down to not cause too large (and unscannable) QR codes.

The customer editing a purchase request to for example try to get products cheaper, and then getting a invalid response QR back, would be equvalient of a customer taking a 20 dollar bill, and trying to make a 100 dollar bill out of it, failing the fraud attempt, and thus losing the 20 dollar bill. So basically, any protection on the request is only for customer protection against themselves, which you can skip if your situation does not require it.

  • $\begingroup$ This makes shivers run down my spine! Not only are you ignoring multiple attack vectors, you're also practically advising to ignore things like public key solutions and even state one could drop the HMAC that you've put at the core of your answer. With no cryptographically secure solution left, how are you planning to secure, protect, or verify the payment process that's partly being put in customer hands? $\endgroup$
    – e-sushi
    Oct 6, 2017 at 3:01
  • $\begingroup$ Even when ignoring the fact that you're messing up the needed security in your described implementation and also ignoring that your note about dropping the HMAC voids 95% of your answer (as it 's based on the use of a HMAC), it still leaves the question how you'ld plan to handle key management for, let's say, 100+ stores with several actively used POS terminals (which defnitely requires more than one shared secret per store) that handle multiple simultaneous sales while interacting with multiple customers and their mobile apps? $\endgroup$
    – e-sushi
    Oct 6, 2017 at 3:48
  • $\begingroup$ @e-sushi : I said you could ignore the HMAC for the REQUEST. The request isn't sensitive, as if a customer does poke in the request, it would make the subsuquent response QR invalid for validation, as the POS wouldn't accept it, thus there would be no gain for a untrust customer. For the response, HMAC is always required (else hackers can fake it). Now I don't understand how my solution would be a problem for "multiple simultaneout sales with multiple customers"? As long as there is one active customer per POS, it isn't going to be a problem. I don't know which other attack vectors you mean. $\endgroup$ Oct 6, 2017 at 4:03
  • $\begingroup$ The REQUEST HMAC is just there to prevent a payment for a purchase that does not exist, to be paid for, as that would leave a customer with money out of pockets but no products (The same as of the customer woulnd't scan the response QR). It can be replaced with a CRC32 or whatever. If a customer pokes and changes the sum for example to get products cheaper, the response QR that you show for the POS, wouldn't validate because when the POS put in all the pending values the HMAC will not be equal to whats expected. Thus only the hacking customer loses out of it by paying for nothing. $\endgroup$ Oct 6, 2017 at 4:13
  • $\begingroup$ To avoid writing a little book that fills up the comment area, I'll point to one of many glitches that I see: "Offline POS … You need a shared key, that is unique per store." — Doesn't that already ring an alarm bell? Hint: You need a unique shared key per POS! (unless you assume every shop to have no more than 1 POS, which is far from RealLife™). Besides that I've already raised key management Qs about your "common secret", which are actually also pretty good pointers to other problems you didn't handle. Maybe if you could clarify that too, the answer might just start making a bit more sense. $\endgroup$
    – e-sushi
    Oct 6, 2017 at 4:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.