Using Public/Private key pair as proof of delivery - security

The problem
I'm working on a mobile application where user A should physically delivery something to user B, and the user A MUST prove that delivered it.
There is a restriction:
User A or User B might be offline on the delivery, so it can not rely on internet connection
My approach
I thought about using cryptography to solve this problem:
When the delivery is scheduled, the following process occurs:
A key-pair is generated, and stored on database.
The private key belongs to User B and should be transfere to his mobile app.
Some well-known+delivery_uuid string is encrypted using the public key, and transferred to the User A.
User A is oriented to only show the encrypted code (in form of a QRCode) if the delivery occurs.
User B is oriented to read the QRCode using the mobile app when delivering.
Since the encrypted message begins with a well-known string, the User B mobile app can decrypt it and verify that the message is OK. The application store the delivery_uuid part if valid, and sends to server-side to keep track as soon as user get internet access.
If the User B try to fake the delivery_uuid, it will obviously not match.
If the User A try to fake the QRCode, the User B's app will not be able to validate the message.
Concerns
The fact of a well-known piece be present on every encrypted message can make it weaker? Considering that the key-pair is used just once.
The public key should NOT be visible to anyone. Only the back-end must use it to create the delivery proof message. Same obviously apply to the delivery_uuid
Sh*t happens. If the user B mobile app somehow crashes and loose the delivery_uuid before sending it to back-end, the user B will need to rely on user A honesty.
How strong must my keys be? Considering the monetary value of the package is low. Is RSA the better encryptation in this case?
I really know that this question is complex, but I really appreciate if someone can help-me with it.
Note: I'm not sure that Stackoverflow is the right stackexchange community to ask about this, please comment if it's offtopic. But since it have something about logic, I think that's the right place.

Seems a little complicated. Why not
UserB (and every user who installs the app to receive deliveries) is issued a public/private key pair. The private key is held only by UserB; if it is lost, a new pair can be issued. Meanwhile, the public key is public, and is stored in a database along with UserB's identity.
Upon receipt of the delivery, UserB generates a simple text document containing the date and time, the QRCode, the name of the person receiving the package, or whatever information is needed. The document also contains the public key. Any format will do.
UserB signs the document with his private key and appends the signature to the end of the document. Now you have a cleartext document spelling out everything that happened, and proof that UserB agreed to it.
UserB shares the document with UserA, and/or uploads it anywhere that is needed, e.g. system of record. Both UserA and UserB can keep an offline copy.
If proof of delivery is ever needed, UserA just needs to produce the signed document.

If I understand the problem correctly, there are three parties here :
The verifying party, which is your back-end.
The selling party, which is User A.
The buying party, which is User B.
Also I do not buy the idea that "The public key should NOT be visible to anyone". They are meant to be, that's why they are called public.
Now, In order to make sure that the item was delivered
By User A
To User B,
We can have the following setup.
The verifying party ( i.e. the backend ) generates a token, associates it with the buyer, the seller, item and persists the info.
The verifying party encrypts the token with User A's ( seller ) public key.
The verifying party encrypts the already encrypted token with User B's ( buyer ) public key.
The verifying party sends the double encrypted token along to User B. Since it has been encrypted with User B's public key, only User B can read it.
User B decrypts the double encrypted token using his private key and saves the result in his device. The result is now encrypted with User A's public key, which means only User A can read it.
When User A comes to deliver the item, User B hands over the encrypted token as acknowledgement. This can be done via a QR code scan.
User A decrypts the encrypted token with his private key and keeps it in the device.
Whenever it is feasible ( in terms of availability of internet ) to prove it to the back-end, User A encrypts the token with the backend's public key and sends it along to the back-end.
The back-end decrypts the encrypted token with its private key and does a lookup in its persistence store, matches the buyer and the seller and completes the verification.

Use signatures and not encryption.
0) The app, as distributed, contains the public portion of a keypair only the server knows.
1) Every user that installs the app generates a keypair, keeping the private key, and uploading the public key to a database.
2) When a delivery is scheduled, the server generates a delivery ID and creates a message containing:
The Delivery ID
The deliverer's (user A's) public key fingerprint.
The recipient's (user B's) public key fingerprint.
A will receive this message and signature prior to making delivery. B can, but need not receive it prior to delivery.
3) When A meets B, A can off-line transfer the message and signature to B. QR code would be difficult, depending on key size, but NFC would certainly work. B can verify the server signature and know that the message has not been falsified or tampered with. A can transfer his public key to B and B can verify its fingerprint via the signed message. A can prove he is who he says he is by creating a signature with the private key belonging to the public key that was just transferred and verified.
4) B can prove who he is by creating a signature with the public key matching the fingerprint in the delivery message. B would have to transfer his public key to A if A didn't already have it, and A can verify it matches the fingerprint stored in the message.
5) B can certify receipt by creating a signature on a message saying so (however you want to format that) and off-line delivering it to A. A can then present this to the server, which the server can verify because it has B's public key.

Let's assume that:
- A has a key pair.
- B holds a delivery UUID.
The goal is to enable B to provide a proof that only A can provide.
As previous answers this can be solved by digital signature. You should let B and A to enchange the necessary information. B should provide the delivery Id, and A must sign it.
You need to hold the public keys of the partners.
You need to provide a way for the information exchange, bluetooth perhaps?
I believe that you may need more than this simple protocol.

After reading the answers, I've formulated a solution to the problem. I'm posting it as answer to know from everyone comments about it validity.
First, let give names to the actors to simplify:
User A is the Seller
User B is the Buyer
The use case
The buyer decides to buy, and make the payment. Now he talk with the seller to combine the delivery.
After payment confirmed, the back-end generates an UUID , and associate it with that transaction_id. Let's call it delivery_uuid. By the business logic, only the buyer have access to it.
The buyer app requests the delivery_uuid to the back-end, which produce a message containing both the delivery_uuid and the transaction_id, then digitally sign this message. Lets call this delivery secret
The buyer app will keep this message in storage.
When both seller and buyer meet, and the buyer checks if the product is OK, then he gives the delivery secret, using a QRCode or NFC.
With the delivery secret in hands, the seller's app can check the delivery secret authenticity (using the application's public key), then store it to send to back-end as proof of delivery as soon as he have internet access.
Now that the back-end have the proof of delivery, the payment to the seller is made.
Considerations
Buyer signature to the message
I think that it's not necessary, since by the back-end conception, only the buyer have access to the delivery secret. If the delivery secret leaked, certainly I'm in a bigger trouble.
Seller signature to the message
Also not necessary, the only concern about the seller is that he needs to send the delivery secret to the back-end. If he loose the cellphone, that's not our problem. If the application crashes and the delivery secret is lost, then we have a problem.
Server signature
This signature allow the seller to make sure that the delivery secret
Belongs to the right transaction, preventing the the buyer to use an authentic delivery token from another transaction.
Have a valid delivery_uuid
Make sure that delivery secret is still valid (we may add a validity to it)

Related

Why does Stripe not use public-key cryptography for signing webhooks?

From the docs:
Stripe can optionally sign the webhook events it sends to your endpoints by including a signature in each event’s Stripe-Signature header. This allows you to verify that the events were sent by Stripe, not by a third party. You can verify signatures either using our official libraries, or manually using your own solution.
Before you can verify signatures, you need to retrieve your endpoint’s secret from your Dashboard’s Webhooks settings. Select an endpoint that you want to obtain the secret for, then click the Click to reveal button.
The last paragraph suggests that the secret is truly something to be treated confidentially. Is there a reason why Stripe doesn't use a private-public key scheme for signing webhook events?
They could keep the private key in their DB, never displaying it on the UI. The UI would only show the public key. Every request made to a webhook by Stripe would be signed with the private key and verified at the receiving end with the public key. This way, malicious actors getting access to the public key would be irrelevant, as they could only use it for verification, whereas now — I assume — an accidentally revealed secret can be used for forgery.
I'm neither a Stripe employee nor a Stripe expert, but I think your question comes from an ideal world where HMAC has disadvantages compared to digital signature from the point of view you're standing on. I suppose the main reason behind this decision is the computational complexity. But for the sake of truth, why you don't consider this problem along with related factors? Stripe prevents replay attacks and publish their ip adresses. Taken together, these measures provide very real protection against message forgery.

How to securely setExternalUserId() in OneSignal?

I'm setting up OneSignal on my website.
As far as I can see, there are 2 ways I can associate a push subscription with my user ID:
I can call OneSignal.getUserId(), which returns a UUID, and make an authenticated call to my web server to associate this UUID with my logged in user on my server
I can call setExternalUserId() to send the logged in user ID and associate it with the subscription on OneSignal servers
The first option is perfectly secure, as one could only hijack my client-side code to send an invalid subscription ID (or another valid subscription ID they have created), which is not a big deal.
The second option though, feels totally unsecure: anyone could hijack the client-side code to send any valid user ID and associate it with its subscription, and therefore receive notifications on behalf of another user.
Is there a way to securely use setExternalUserId() while preventing a user from associating their subscription with another user?
The only secure scenario I can think of is if my users had UUIDs as well, instead of sequential IDs, and these UUIDs were kept secret (i.e. never exposed publicly on the website).
Any other scenario I can think of sounds plain insecure.
Did I miss something?

How can I implement exclusive/invite-only user registration?

I'm creating a site with user auth, and I plan to limit access using either invites or some other method.
The site is built on Node, Express, and MongoDB. I plan to use Passport JS, mainly because it's the only method I've learned (this is my first personal project).
The only idea I have is a "secret code" on the registration page. Thus only those I've told the code can register. I have a feeling there are more elegant or secure ways to handle this, and would love any recommendations!
I think your idea is correct in principle - it's the same method used for registration/beta keys for games. You generate a unique 'key' for each user you invite to register. They register with that key and it is marked 'used' in your database; this prevents other users from discovering that key and re-using it.
You could also use email addresses in essentially the same way. The email address that is used to register must be on your 'invite list'. And when you 'confirm' an address by sending a 'click this link to confirm' email you will have to generate another key for authenticity.
Therefore, upon registration with an invited email, you could generate a key as follows:
require('crypto').randomBytes(48, function(err, buffer) {
var key = buffer.toString('base64');
// then save the key with the new user in the database
});
Then send an email with a confirm link containing the key, for example:
https://www.mywebsite.com/users/confirm_email/{key}
This link would call a 'confirm_email' action on your server, look up the specified key, and enable the account it is associated with.
You might want to add an expiry along with each key creation for a bit added security. Maybe only 24 hours to confirm the email.
You don't need any secret codes if it's with invitations :
When someone invites someone else, you store the email invited somewhere. You simply need to check that a new user is "on your guest list" when he tries to register.
Of course, to be "secure" this approach assumes you actually checks that an email address properly belongs to the user that registers, for instance with a verification email, as done usually. The point is that you don't need an additional token.
One solution I can think of just generates the token using the senders token (use jsonwebtoken signed with expiry time and sender's token). Now when the user who is invited will receive the link, let say: http://localhost:5000/invite/${token} and the link is clicked then a GET request will be sent to the server so catch that request and then in that request in the backend decode that token and check your user database if that user exists i.e sender and token is not expired then it's valid invitation so now directly redirect the invitation receiver to the register page else send the message that invitation is not valid.
Hope this help.
Let me know your views.

How to secure an app built on phone-number registration?

I want to build a mobile app that requires the number of the user to be filled in. After that, the phone number is sent to a server, the server generates a random verification code that corresponds to this phone number. Then, this verification code is sent via SMS to the user. Next, the user sends the verification code back to the server to ensure that he/she has entered his/her real phone number and not anybody's else.
I was wondering how do you really authenticate against the server if you only have a phone number and nothing else? I mean, in the typical scenario you have a username and a password that are checked on the server and if both of them are correct you can have access to the server. But in the case of a phone-number registration, you have only a phone number and if you authenticate with it only, it means that anyone who knows your number or just picks it out, can pretend to be you.
If you send some sort of a unique device ID, that means that you won't be able to use your existing account anymore, for example, if you happen to change your device with a new one.
So, how do you solve this issue?
The pattern is always: client provides proof of something they have, in return they receive an identifying token. In a typical username/password scenario, this means the user proves that they have a secret (username + password), in return they'll typically receive a session cookie. In your case, the user proves that they are in possession of a specific phone, in return you give them a session token or other identifying token. The client holds on to this token and uses it to identify themselves to the server.
You're relying on the principles of the telephone system to make sure that's a uniquely identifying characteristic. You're basing your security on the assumption that only one person can receive messages for a specific phone number at any one time, and that you need to be in physical possession of the phone at the time of login to complete the proof. Of course you require this proof every time the user logs in. You do not let them register once with an SMS-loop, then afterwards you just ask them for their number and let them through.
If a user wishes to log in, they must proof they're in physical possession of the phone in question using the SMS-loop, then they'll receive a token. Period. That's the way it goes. No other way. The client (app) must hold on to the token for as long as it wishes to stay logged in. Obviously, you probably want this to last for quite a while and not require the user to do SMS confirmations all the time.
This obviously brings us to the topic of token theft, which can be a real issue. The token must be kept secret, since it essentially allows proof-less authentication. You may want to think about signing that token using some unique identifier specific to the device it's for, or encrypting it while it's stored on the device or other measures to make sure it can't be nicked from the device while it is stored on it.
As deceze points out, the best way to ensure the comm is safe is to use a temporal token signed with the device id. If the user logs out, changes device or reinstalls the app then they must go through the SMS verification process again to ensure the SIM cards is still in their possession. Keep in mind that the SIM card and device have different udids. To simplify this you can use RingCaptcha SDK on your app to generate the token, verify the user possesses the SIM card and store that token or id temporarily. Use the phone number as an identifier - similar to a username - and the temporal PIN code as the password. That pair plus the token will give you enough security that the device and SIM card are joined.

Non-repudiation is duplicated?

When we talk about security we have the following requirements:
authentication
integrity
Non-repudiation
Isn't the third requirement included in the first two? If we know A sent the message (authentication) and it has not been changed since A sent it (integrity) then how can A repudiate sending it?
Please don't talk about dig-sig as it's in the technical level. I'm talking about the business requirements.
Neither authentication nor integrity protections prevent replay attacks. A malicious user can capture a signed and encrypted message and post it multiple times. Therefore a party can repudiate having sent the same message multiple times.
Making each message unique using timestamps and/or nonces addresses this and is therefore used for non-repudiation in combination with signing and encryption.
Non-repudiation is different than integrity and authentication because it implies that the sender is accountable for the contents of a message.
There are many systems that use a key for authentication and integrity, but the authenticated content doesn't mean anything. For example, suppose that in order to authenticate you on my system, I send an unpredictable challenge and ask you sign it and send it back. If the signature is valid, I trust that you know some secret and therefore are who you claim to be. I'd require the key you use for signing these challenges to be designated for digital signatures, but not necessarily for non-repudiation.
Now suppose instead of choosing a random challenge, I try to trick you by sending the challenge, "I will pay erickson one million dollars." If your system signs that, do I have a claim to a million dollars? The signed message is authentic and not tampered with, but unless you signed it with a key flagged for non-repudiation (for example, setting this flag in the key usage extension of an X.509 certificate), you can deny that you were aware of its content and reject my claim.
Non-repudiation makes sense for things like a signatures on documents in a business transaction—cases where you are obligating yourself to some action or payment.
With authentication and integrity, what you can achieve is message authenticity, ie. the recipient can be confident that the sender ID and message content are genuine.
Non-repudiation, on the other side, ensures that none of the involved party can deny having sent or received the message. In the previous scheme:
While the recipient can prove that the sender has indeed sent the message,
The sender itself has no proof that the recipient actually received it.
Non-repudiation systems will therefore include some kind of acknowledgment in order to provide these proofs.
Usually the three security requirements are CIA, i.e.
Confidentiallity
Integrity
Authenticity
But concerning non-repudiation, authentication and integrity dont necessarily provide non-repudiation since integrity says some message has not changed when traveling from point X to Y.
Authentication can tell you that some message is sent by anybody who has knowlegde of some (shared) secret that should be only known to a person.
Imaginge a virus stealing private keys from Alice, in that case you can have integrity of message X and authentication that the message is from Alice (although one can argue if this is real authentication) however, some eavesdropped used the stolen private key to send the message.

Resources