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.

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.

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 will have the form https://share.padlok.app/<hard to guess ID>/<hard to guess KEY>

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;
  ...
}

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 is available for free (with in-app purchases) in the App Store.