The 5 steps to end-to-end encrypted photo storage and sharing
A semi-technical overview of how Textile is handling on-device photo encryption and shared photos.
At the center of the Textile Photos app is a powerful tool-chain for creating private keys, encrypting personal photos, and ensuring your data remains private and secure. We’ve noticed that a lot of existing applications are pretty hand-wavy when it comes to explaining how their encryption works. We believe that through open source and transparent approaches we can actually build a more secure and trustworthy app.
Today, we want to share an overview of how our on-device photo encryption works. In short, Textile Photos is an end-to-end encrypted photo application. But one of our biggest goals with this best-in-class encryption, is that users don’t have to notice. So for the curious, here are the 5 steps your photos go through, from import, to encryption, to sharing your captured moments with friends & family — automatically secure.
Step 1. Your private seed and key pair
Okay, so this one happens before you share a photo, but it’s an important step that we want to explain. Every user who downloads the Textile Photos app is provisioned a new mnemonic phrase (sometimes called a seed phrase), which is then used to generate a new public/private key pair for the user. The seed and private key are both private to the device and are never shared anywhere off the device. This all happens right on your phone.
If you’ve already used Textile Photos beta, you may not have seen your mnemonic or private key anywhere, but you will soon. We are working on seed-based account recovery, so if you drop your phone in a mud puddle or lose it, you’ll be able to recover everything you’ve created. This is a work in progress, but we hope to have it released this quarter™️. At that time, we’ll be creating more obvious steps for users to ensure their accounts are recoverable. If you want to follow the project to update the account creation and schema, check out the project on GitHub.
- Seed phrase generated using the BIP39 specification for generating mnemonic sentences (of varying levels of entropy) useful for the generation of deterministic binary seeds. This happens directly on the app installed on your device, no servers involved.
- Public/private key-pair derivation uses the ED25519 signature algorithm (which uses elliptic curve cryptography). Again, to keep the user totally secure, this happens directly on the device.
Step 2. Per-photo encryption key
Okay, now the Textile Photos app is ready for its first photo. When you go to add that first photo, the app is going to do a couple of interesting things. The first among them is that it will create — entirely on your device — a new single-use key. This encryption key will be associated only with this one single photo, and stored in your app for safe keeping. As before, it doesn’t leave your phone (until you share it).
Over time, we plan to improve this setup even further. For example, we have a lot of room to explore key-based permissions, where you may grant only partial access (say by resolution) to a photo you share. If you’re interested in exploring these ideas with us, feel free to get in touch, jump into our code, or just chat with us on Slack, we’re happy to discuss! And for those less familiar with block ciphers and ‘crypto-speak’, take a look at this article on Symmetric Encryption with AES for a good starting point. Also, this StackExchange site is gold for learning about the (sometimes very confusing) world of cryptography.
- Array of random bytes (44 bytes long to be exact) is generated, with 32 reserved for the encryption key and 12 for a nonce. U̶s̶e̶s̶ ̶a̶ ̶p̶a̶i̶r̶ ̶o̶f̶ ̶K̶S̶U̶I̶D̶s̶ ̶t̶r̶u̶n̶c̶a̶t̶e̶d̶ ̶a̶t̶ ̶4̶4̶ ̶b̶y̶t̶e̶s̶ ̶t̶o̶ ̶g̶e̶n̶e̶r̶a̶t̶e̶ ̶t̶h̶e̶ ̶r̶a̶n̶d̶o̶m̶ ̶b̶y̶t̶e̶s̶.̶ ̶K̶S̶U̶I̶D̶s̶ ̶a̶r̶e̶ ̶a̶ ̶n̶i̶c̶e̶ ̶w̶a̶y̶ ̶t̶o̶ ̶g̶e̶n̶e̶r̶a̶t̶e̶ ̶g̶l̶o̶b̶a̶l̶l̶y̶ ̶u̶n̶i̶q̶u̶e̶ ̶I̶D̶s̶ ̶s̶i̶m̶i̶l̶a̶r̶ ̶t̶o̶ ̶R̶F̶C̶ ̶4̶1̶2̶2̶ ̶U̶U̶I̶D̶s̶ ̶(̶b̶u̶t̶ ̶b̶e̶t̶t̶e̶r̶)̶.̶ Oop, we have now removed the time component from the securely generated random number. Again, all these steps happen right on the device, so no server ever needs to know your private keys.
- Photo is read from device’s storage, and decoded to remove Exif information and correct any orientation issues or format-specific encodings it might have to deal with.
- If the photo is in HEIC, HEIF, or WebP format, it is converted to JPEG (for now) at a few optimized resolutions (thumbnail, preview, etc).
- Bytes of the photo and metadata (as well as the phone’s public key from Step 1) are encrypted using AES-256 GCM authenticated encryption — 128-bit AES block cipher wrapped in Galois Counter Mode, with a standard nonce length of 12 bytes, using the KSUID-based key.
Step 3. On-device content addressing
Now that your photo (plus thumbnail, metadata, etc) is encrypted with its dedicated single-use key, the next step is to make it available on the immutable, decentralized web. At Textile, our entry-point to this decentralized web is through the Interplanetary File System, or IPFS. We’ve talked a lot about IPFS before, and how it changes the way we access content online. For the purposes of this post, it is sufficient to say that we use IPFS to hash the previously encrypted data to generate it’s content address. This content address is essentially a cryptographic fingerprint of the underlying encrypted data, and gives your device a nice way to reference that data, regardless or where it is actually stored. With this information, your phone has a permanent address for your encrypted photo data and key, at which point Textile Photos stores these final bits of information in your private Textile Wallet. Want to learn more about the Textile Wallet? Tune in for a future blog post on the exciting world of the Textile Wallet, also coming soon™️.
The technical bits of this step are actually pretty well-covered in a previous post of ours about what’s really happening when you add a file to IPFS, but the general steps are…
- Raw encrypted bytes from previous step are run through a SHA256-based multi-hash hashing function, to produce a digest. This digest is guaranteed to be cryptographically unique.
- These various digests are arranged in a directory-like structure as an Interplanetary Linked Data (or IPLD) store. This data structure is essentially something called a Merkle DAG, or directed acyclic graph.
- A reference to the top level hash of the above IPLD hash tree is then stored in your Textile Wallet (see this article for details on hashes and CIDs).
Step 4. Shared keys for group Threads
Now you probably want to share that photo with someone? So the way this works ‘behind the scenes’ in Textile Photos is going to take a bit more explaining. The full explanation is part of a longer post on Textile’s Thread concept, due out later this week. The short explanation is that, in Textile Photos you can create private groups (or Threads) for sharing photos, comments, etc. Each new Thread you create or join will get yet another cryptographic private/public key-pair! This key pair is generated on the device that initially creates the Thread, and is only shared with those devices that are explicitly invited to the Thread. So, who has access to your Thread’s key-pair? Only the device it was generated with, and those devices it explicitly shares it with. You don’t want Textile to have access? Fine by us! Keep your private stuff private, and we’ll do the same.
The technical bits in this section are purposefully limited, as we’ll cover these steps in more detail in our next post. Having said that, the general steps are…
- User creates a new Thread. This thread automatically gets its own public/private key-pair, again using the ED25519 signature algorithm mentioned in Step 1. We use ED25519 here because it’s fast, compact, secure, and widely used. See the EdDSA Wikipedia page for more details.
- The app also automatically adds this new Thread to the user’s private Textile Wallet. At this point, this information lives on-device only. Safe and sound.
- User can now send that key-pair to anyone they want to invite to the Thread. This can be done directly from within the app or by sending an external invite — sending the Thread’s private key, encrypted using a new invite-specific key via the AES block cipher algorithm mentioned in Step 1. They can then send that encrypted key using some external means of communication, like secure private chat, end-to-end encrypted email, etc.
Step 5. Encrypting your photo’s key (and sharing the result)
Now, with the Thread keys in hand, we need to somehow get the photo set (and metadata) key to the other Thread peers. The photo is already encrypted, and has an immutable IPFS hash. So, we take those two pieces of information — photo key and hash — and encrypt them with the Thread’s private key. Next, we wrap them up in a message structure, and sign this with the user’s private key. The whole message and envelope is then shared with each member of the Thread via a direct peer-to-peer (p2p) communication if possible.
‘Over the wire’ communication (while the photo envelope is in transit), is also protected by a layer of encryption. In fact, you might call this our ‘first line of defense’ against would be network observers. This means that all connections use secure sessions to encrypt all traffic. Something like a TLS-like handshake is used to setup the initial communication channel, so you might think of this whole thing like an SSL session.
At the other end of the wire (on the peer end) the app accesses the signed message data, but only after they’ve verified the message signature with the sender’s public key. Assuming the message is legitimate, they’ll then decrypt it with the Thread key you shared with them before, and then decrypt all the internal photo data with the single-use key inside that package. Amazing! Again, no central servers ever knew any of those keys… just a bunch of phones! So to reiterate… who has access to your private keys? Only the device they were generated with, and those devices it explicitly shares them with. You get the idea by now.
- Using the private key associated with the target Thread (from the previous step), the app encrypts the photo set hash and single-use key needed to decrypt the photo set.
- The app then sends out this package — signed with their own app-level public key — to all the members of the Thread.
- Over the wire, the whole message is encrypted using the secio stream security transport.
- In the background, all Thread members essentially fetch the data via IPFS, and then peel back (i.e., decrypt) all the various layers of encryption with the keys that they have securely stored on their devices.
That’s it… for now
Obviously 99% of this is happening in the background, without any real user intervention. And that’s really the beauty of Textile’s approach here. Secure, modern photo encryption without user intervention. Just safe and easy by default. At this point, you might be scratching your head and wondering, how did the actual sharing happen? Well, you’re in luck, next week we’ll be taking a deep dive into the Thread concept and its underlying technology.
In the mean time, there are a few things that we’re working on right now, to make this end-to-end interaction even more secure. For example, rather that just signing the Thread messages, we’ll soon be encrypting the whole package. This means that not only is the photo data, metadata, keys, and other content encrypted, but all thread history will now be fully encrypted. This will make it harder for some nefarious network observer to traffic types to a specific user.
Additionally, the seed-based account recovery work that is already underway will drastically improve the way the top-level keys are generated and handled within the app. Allowing more control and better recovery options.
No post on encryption would be complete without a statement along the lines of: “The encryption framework used in Textile Photos has not yet been externally audited. Textile Photo is still in its Beta release, if your life depends on the security of our application, please use a more mature platform for now”. With that in mind, we have gone to great lengths to make sure it is safe and secure.
Interested in trying some of these ideas out? Maybe you want to field-test our encryption system? Well, make sure you sign up for the Textile Photos wait-list if you haven’t already, to get early access to what we’re building. While you’re at it, take a look at some of our previous posts on some of the underlying technology behind Textile Photos, or reach out on Slack, Twitter, or GitHub to share your thoughts, questions, or cool decentralized technologies that you are building. If this work is really your thing, hit us up on email@example.com, we’d love to hear from you.