The Web Cryptography API

Tim Taubert explores how we can keep secrets with JavaScript

Due to its nature as a dynamic language, it is surprisingly difficult to safely implement cryptographic primitives in JavaScript. To bridge this gap, a W3C working group was formed to design an API that provides these basic building blocks to web pages, apps and workers. Modern browsers already ship with everything that is required to build such an API, as enabling secure connections to servers via HTTPS requires that same functionality.

The Web Cryptography API, at home under the namespace window.crypto, accepts input data in the form of TypedArray objects. As cryptographic
operations can be quite expensive, all its methods return promises resolving to ArrayBuffer objects to not block the browser for the length of the computation. A reliable source of randomness is essential, and thus there exists window.crypto.getRandomValues() to fill a given typed array with
pseudo-random bytes:

var buffer = new Uint8Array(16);
crypto.getRandomValues(buffer);

The majority of the API is exposed through window.crypto.subtle. The word ‘subtle’ indicates that most of the available algorithms have subtle usage requirements that need to be fulfilled to provide the desired security guarantees. Implementing cryptography is hard; the tiniest mistake can
break the security of your application.

Enough warnings! Let’s take a look at another example. When downloading files over the internet, to ensure integrity it is important not only to check for corruptions, but also for malicious modifications. The WebCrypto API makes it easy to provide a checksum computed by a cryptographic hash function along with the file:

crypto.subtle.digest(“SHA-256”, data)
.then(function (digest) {
console.log(digest);
});

The above example will compute an SHA-256 digest over the given data argument and, as soon as the operation finishes, log the resulting ArrayBuffer to the browser console. Encrypting data can be a little more complicated, depending on the algorithm you choose. AES-GCM is a standard that uses a random component, the initialisation vector (IV), to ensure equal plaintexts will never have the same ciphertexts:

var iv = crypto.getRandomValues(new Uint8Array(16));
var alg = {name: “AES-GCM”, iv: iv};
crypto.subtle.encrypt(alg, key, data)
.then(function (ciphertext) {
console.log(ciphertext);
});

A random key and IV are used to encrypt the given data argument. The ciphertext logged to the console can later only be decrypted with the same key and IV.

The current versions of Firefox and Chrome support most algorithms listed in the specification, though both implementations are still works in progress. In the short term, we will most likely see the addition of algorithms such as Curve25519. In the more distant future, the API might be shaped by W3C’s Streams API — encrypting and securing streams of data would be an exciting extension.

Tim works on Firefox and helps implement the WebCrypto API. He enjoys cryptography and running, as well as craft beers and riding things with two wheels


This article originally appeared in issue 268 net magazine.