With my package hoodie-plugin-store-crypto reaching version 3.1 and the year 2019 coming to a close, I wanted to write a post about the next feature I'm going to implement:
A method to encrypt any JSON Object, without saving it.
Wait, thats it‽
Well, yes. But the thing is, hoodie-plugin-store-crypto is a plugin for the Hoodie project that enables developers to store encrypted documents in their users data.
This is realized by it being a warper around Hoodie-client-store with the same API. But with the difference, that every document written with it, will be encrypted. Here lies the problem: You had to use your users database! You could have used _local/-docs, but this would still write to disc.
API (preview)
This new capability will be implemented using 2 methods:
cryptoStore.encrypt(object[, aad])
cryptoStore.decrypt(object[, aad])
Both will return a Promise resulting in the encrypted object. And they will use the same crypto-key as the other methods.
// example API: hoodie.cryptoStore.encrypt(object[, aad]) .then(encryptedObject => { // do something with it }) hoodie.cryptoStore.decrypt(encryptedObject[, aad]) .then(decryptedObject => { // do something }) // This function will be similar to hoodie.cryptoStore.add async function add (object) { if (!object._id) { object._id = uuid() } var id = object._id delete object._id var hoodie = object.hoodie delete object.hoodie // Encrypt object and use its _id as the aad const encrypted = await hoodie.cryptoStore.encrypt(object, object._id) object._id = id object.hoodie = hoodie // Store the encrypted object const storedObject = await hoodie.store.add(encrypted) // Decrypt the stored object const decrypted = await hoodie.cryptoStore.decrypt(storedObject, storedObject._id) return decrypted }
Why
For one: there a many uses cases for this API.
But, second, for now you had to implement your own encryption stack:
- Check if crypto.subtle is available and use browserify-aes and pbkdf2 if not.
- Generate a salt.
- Generate a key.
- encrypt and/or decrypt.
- And test all of it.
All of this is already done by my plugin. So why re-implement it?
Some use cases
In combination with cryptoStore.withPassword it will be really be flexible.
You will be able to use it with any password. Be it from your App's bundle, an API, or some stored in a document. Remember to change them frequently!
Please remember that those are only ideas. And not tested by me! And I have no background in cryptography! All I'm doing is package it in a nice to use API!
Use at your own risk!
Now follows some uses cases I could think of:
Unlocking
With a password included in your bundle (or from an API), you could store the users password in the sessionStorage (which will never be saved on disc) and unlock your users cryptoStore on reload.
With a password included in your bundle (or from an API), you could store the users password in the sessionStorage (which will never be saved on disc) and unlock your users cryptoStore on reload.
This useful for example if the user is a mobile user. When they switch the browser tab or app, your app might be removed from memory. But things in sessionStorage will remain. Watch a video about the Page Lifecycle API.
// combine it with usePassword hoodie.cryptoStore.usePassword(yourSecretKey, yourSecretSalt) .then(result => { const { store, salt } = result return store.encrypt(object) }) .then(encrypted => { window.sessionStorage.setItem('secret', JSON.stringify(encrypted)) })
Both could be done using encrypted _local/ documents. But they are stored to disc (although only local). And this is for some industries and Apps an absolute no-go!
Only one password
If you don't what to know your users encryption password, they have to enter two passwords. One to sign in and one to unlock the encryption.
If you don't what to know your users encryption password, they have to enter two passwords. One to sign in and one to unlock the encryption.
But with the new methods, you could generate a secure password for your users. And use it to sign in.
Then encrypt the generated password with password the user has entered. Store it in your _users database.
API end-point could then return the encrypted password. Which will then be decrypted by the users password. And with the decrypted password you sign your user in.
async function signIn (username, password) { // fetch the encrypted password from the api const response = await fetch(`/api/sign_in/${username}`) const encryptedSignInPw = await response.json() // generate an instance of the cryptoStore API with the password // the user entered and the salt that was returned by the API. const { store } = await hoodie.cryptoStore.withPassword( password, encryptedSignInPw.salt ) // decrypt the sign in password const signInPassword = await store.decrypt(encryptedSignInPw) // sign in with the generated sign in password await hoodie.account.signIn({ username, password: signInPassword.password }) // and unlock the cryptoStore await hoodie.cryptoStore.unlock(password) }
Closing
Those two small methods open up many possibilities.
But remember: I'm not an expert! You have to do your own risk analysis and testing, if you want to implement any of the examples!
I hope your 2019 was great and that your 2020 will be even better! Have a happy new year 🎆!