I'm building a mobile application that transfers points between users via QRCode. I'm concerned about security and am looking for effective simple yes secure algorithm to use. The scenario should be something similar to this.

  1. User A wants to transfer X points to User B.
  2. User A generates a QR code on his mobile application, embeds the points number(X) and userA.id in the QR code.
  3. User B reads the QR code, sends {points numver:X, from=userA.id, to=userB.id} to the server.
  4. User A can generate the QRCode while offline, where user B must be online to read the QRCode.
  5. The server receives the request and transfers X points from user A to user B.

The technical scenario we have for making this:

  • The server has a global value named salt. salt is random and auto generated every day.
  • In the server, every user has the following fields:
    • id: Auto Incriminate number.
    • userKey: md5( id + salt ) => changes every day.
    • generatedQRCodesNumberForToday: a number represents how many times the user generated a QRCode today, max value is 10, the value resets every day.
    • N: Array of already uses numbers in the current day while transferring points, the array resets everyday.
  • Every day, the user claims a new userKey.
  • Every user have a counter in his mobile application represents generatedQRCodesNumberForToday, this number is synced with the server whenever the user is online, and this number increases whenever the user creates an new QRCode.
  • If user A wants to transfer X points to user B:
    • User A creates a QRCode that has {from:userA.id, numberOfTrile:md5(userA.userKey + userA.generatedQRCodesNumberForToday), points:encode(userA.pointsToTransfer, userA.userKey)}.
    • User B reads the qr data, adds to:userB.id to them, and sends them to the server.
  • On server side:
    • The server receives the from field and knows who is the sender.
    • The server receives the to field and knows who is the receiver.
    • The server receives the points field and decodes is using the sender userKey to know the amount of points.
    • The server receives numberOfTrile field. The use of numberOfTrile field is to make sure that no user will generate the same QRCode more than once. The server tries to match numberOfTrile with {md5(from.userKey + '1'),...,md5(from.userKey + N),...,md5(from.userKey + '10')} where N was not used before, if there's a match then the transform process is approved and the number that matched the concatenation N is marked as not valid for future use.

I want to know if my process is secure or it has any possible security vulnerabilities.

  • if I go offline just before whatever your midnight changeover is, I'm stuck until I get back online again. There's also a race condition between me generating the code and it being redeemed on the server near midnight. Normally you pass all the information you signed with a key - it keeps you from having to hit the database a bunch of extra times, among other things. Why only 10 transactions a day? What happens if the app crashes after I generate the QR code, but before my buddy can take a picture? Do points have to be redeemed immediately, or what happens if I scan one later? – Clockwork-Muse Dec 28 '18 at 8:48

You are overcomplicating this. A counter of QR codes per day, daily salts, values that need to be synchronized...

I would simply design the application having an ECC keypair. When A wants to give N points, it makes a 'coin' with the information (timestamp, sourceUser, points, expiry), signed with its private key.

Then user B scans the coin and presents that to the server, «I am user B and want these points, here is the check signed by A». The server just needs to verify that

  • it has a valid signature from A
  • the last transaction by A was earlier than this coin timestamp (just a counter would do, too) - in order to avoid double spends (I am assuming that a valid transaction invalidates all previous ones that might be pending and somehow the server didn't see yet)
  • A actually has enough points to give out

Since A and B will be at the same place, the expiry an be as short as a few minutes, which is nicer than the daily salts (what if A is offline and meeting B after midnight?). You need to allow a bit of leeway for unsynchronized clocks between devices, but since the points would be redeemed almost instantaneously, at the point it starts being rejected it would be easy to understand by the end user, too when you tell them "This offer was only valid until XX:YY, it's now XX:ZZ, please check the clock time is set right."

  • ...although given the last paragraph in the question, I'm not sure we can rely on in-order-only – Clockwork-Muse Dec 28 '18 at 8:43

Step 1: The auto incremented id implies you're worried someone would know the id one day. In that case they would know it on other days too.

Step 2: If the salt isn't long enough, someone can find it by brute force if they know their own id, and then they'll be able to use the id they got in step 1 to create the md5 which is the userKey.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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