In this article we will write one of these ubiquitous Todo applications with Ionic. The app will store the data in localStorage on the client, but not in clear text, instead it encrypts the data before storing it.

First I wrote the application with the Web Cryptography API. A web standard developed by the W3C and built into modern browsers. When you look on caniuse.com the browser support looks very promising.

Unfortunately, at the time of writing, the app did not run on Safari. Safari hides the Web Cryptography API behind a prefix. That's not a problem and easy to solve. The following snippet maps the prefixed object (crypto.webkitSubtle) to the standard (crypto.subtle).

Because I wanted an application that runs on all platforms I switched to asmcrypto.js, a cryptographic library that is written in JavaScript. But in my opinion the Web Cryptography API is the way to go in the future when all browser support it and implement a similar set of algorithms. In this blog post you see the performance benefit of the Web Cryptography API, which is implemented natively in the browsers, compared to cryptography libraries written in JavaScript. If you are interested, here is the source code of the Todo app written with the Web Cryptography API. It works on Chrome (also Chrome on macOS) and on Android.

The app we develop in this post contains three pages. The first page the users sees is the Password (html / ts) page. On this page the user enters his password, the app derives the AES key from it and uses that to encrypt and decrypt the todo entries.

And the third page (Edit: html / ts) contains a form where the user can create and update todo entries.

The application is based on the Ionic blank starter template and I added the asmcrypto library to the project

npm install asmcrypto.js

I won't go into the implementation details for these pages. They are written with straightforward Ionic / Angular code and there are already a lot of tutorials how to write a todo app with Ionic (here, here, here).

It first loads the last used id from localStorage. Every time the user creates a new todo object the application increases this id by one and assigns it to the id field of the todo object. Next the method calls the deriveAesKey function that creates the AES key.

PBKDF2 is an algorithm that can be used for deriving a key from a string. The application has to specify a salt, the number of iterations and the number of bytes the function should return. Because we want to use AES with a key length of 256 bit, PBKDF2 has to generate a key with 32 bytes. In this app the salt is just a constant string, that should be okay for this single user application. iterations specifies how many times the PBKDF2 algorithm should run. Higher is better, but slower. Recommended is a value of at least 1000, the app uses 4096.

This method fetches the data from storage. When the app is started for the first time, the result will be undefined and the method then assigns an empty new Map instance to the todos instance variable. If there is data stored in localStorage, the app decrypts it with a call to the decrypt method.

To decrypt the data the algorithm needs the nonce, that was created during the encryption process and prepended to the encrypted data. The method separateNonceFromData splits the stored blob into the nonce and the encrypted data. The decrypt method then calls the AES_GCM.decrypt method from asmcrypto to decrypt the encrypted data. This method returns the decrypted data as an Uint8Array object. Back in the decryptTodos method the application needs to convert this buffer into a string (bytes_to_string) and then parses it to a JavaScript object.

The encrypt function first creates the nonce, an arbitrary number that may only be used once. Then it calls the AES_GCM.encrypt function with the plain text, key and the nonce as parameters. After the encryption, the function merges the nonce and the encrypted data into one buffer (joinNonceAndData), because the application needs to provide the same arguments in the decrypt step. Back in the encryptAndSaveTodos method the application stores the encrypted blob into localStorage.

Notice! Security is hard and I'm not a cryptographic expert. If there is code or a description wrong please tell me so I can correct it. Thanks.