Building a share feature with privacy in mind
One of the main reason for me to build Padlok was data management. I didn’t want anyone to retrieve my friends addresses and codes. As with any contact related infos, I may have trust issues about them.
And so should you!
That’s also why Padlok development is focused around data privacy and security. Data is stored on device only, an synced with iCloud. And early share feature was simply generating a small text to be shared. But this experience was not enough.
Padlok
Still looking for the codes?
iOS share experience features
Apple came up with a lot of share experience features within the last years.
Universal Links opens a link directly in the application for a full native experience when it’s installed on device.
App Clips transforms a simple link share experience to a full native experience, and maybe can make someone to download the full app.
Finally, a link is a link, and can open the shared infos within a webapp when all of the above is not available (older OS, or any non-iOS device). Plus Smart banners might redirect users to the App Store when the above experience failed on iOS.
The conclusion is clear: sharing a link, with the above experience changers are a must have for a modern iOS app.
Problems with links
First problem is that link are public. And without any authentication service, anyone with the link could access the data. The solution here is to generate hard to guess links.
Links will have the form https://share.padlok.app/<hard to guess ID>/<hard to guess KEY>
Updated on December 4, 2022
After a comment made by Valtteri Huuskonen, links were updated to format like: https://share.padlok.app/<hard to guess ID>#<hard to guess KEY>
Thanks to that, the key, that is our secret, is never sent to server, because it’s now using URI fragments.
Next problem is that some might try to guess urls anyway. So we need to have a restriction mechanism that would detect and prevent url iteration to be possible in reasonable time.
API that serve data will have a limit to prevent a single user to make too many fetch attempts.
Then, by default, urls are logged and stored on the webserver itself. And I don’t want to possess any share url what-so-ever. So I decided that for the shares urls, logs should be disabled.
location / {
access_log off;
(...)
}
Updated on December 4, 2022
Even if the new link format prevent key leaks, logs are still disabled to be sure that older links do not leak links!
The final problem is about data. I do not want to possess any user data on my server. But somehow, I have to, since all the data cannot be stored in the URL.
End-to-end encryption for data
Modern encryption result is indistinguishable from noise. And it’s exactly the kind of data I expect to store on my server, associated to a generated identifier. And to make sure the process is completely secure, I don’t want to store the key material. Key material will be in the url itself, that, for recall, will not be logged.
Finally, the key will never be managed by my server. I want to make encryption end-to-end.
Since I wanted the encryption an decryption to happen either on the iOS app, or from the webapp client side (a.k.a in JavaScript), using a cryptographic standard that’ll be easier to set up.
AES-GCM will be the encryption algorithm here. With a shared key of 256 bytes; this encryption method is one of the widely adopted for its performance and confidentiality.
So we need to share a 256 bytes key in the URL. A way to do that would be to have the key as is in the URL, encoded in a base62 or base64-like format. But in that format, the key takes up to 44 characters. It’s a bit long.
To reduce the URL size, we need to reduce the entropy of the key. I decided to do so using PBKDF2 key derivation.
The input for generating the key is now up to three variables:
Variable | Origin | Storage |
---|---|---|
passphrase | Generated string | Provided in the URL |
nounce | Generated data | With cyphered data on the server |
iteration count | Generated number | With cyphered data on the server |
And from those, we can now generate the key
The final process
Encryption
We start to generate the key parameters:
- Generation of a 12 characters passphrase
- Generation of an iteration count >= 1000
- Generation of a key nounce of 8 bytes
Before making the key:
Passphrase + Nounce + Iterations -> Key
Then use that key for encryption:
- Generation of a initialization vector (IV) of 16 bytes
And perform the encryption:
Data + IV + Key -> AES-GCM -> Cypher
And we store on the server:
- Cypher + IV
- Iteration count
- Key Nounce
Decryption
For the decryption, we retrieve from the server the data described above, and we use the passphrase from the URL:
We can then make the key again:
Passphrase + Nounce + Iterations -> PBKDF2 -> Key
And then decrypt the data:
Cypher + IV + Key -> AES-GCM -> Decrypted data.
Open-Source implementation
Most of the implementation for Padlok Share feature is Open-Source on my Github:
- The encryption/decryption for iOS, powered by CryptoSwift is published within my Padlok-ShareKit Swift library.
- The decryption mechanism in JavaScript is published along with the whole webapp (share.padlok.app) in Padlok-Share repository.
- The api allowing to store the data within my server, powered by Vapor is published in Padlok-API repository.
Making all of this code open-source is for me a natural decision of both transparency, and security. Transparency because anyone can check that I actually do what I describe within my codebase; and security because having other minds to challenge my implementation can help to resolve possible issues in security.
Padlok
Still looking for the codes?
Don’t miss a thing!
Don't miss any of my indie dev stories, app updates, or upcoming creations!
Stay in the loop and be the first to experience my apps, betas and stories of my indie journey.