Short Introduction to SJCL

The Stanford Javascript Crypto Library, or simply SJCL, is probably the best option available right now for using cryptography on the client-side: the project started in 2009 as a paper describing how to implement a secure and fast crypto library for web browsers, including, for instance, a CSPRNG algorithm and symmetric encryption. Today, it also has public-key crypto, hashing, and ECC primitives. It’s very small (just 37kb uncompressed w/ ECC), the code is pretty clean, and it’s being maintained by various contributors on GitHub. One of the biggest problems right now is the auto-generated documentation, which isn’t super helpful, but that’s not a big deal since you can always read the sources.

Let’s start with a very simple example using the convenience functions to easily encrypt and decrypt data (using AES):

1
2
var msg = sjcl.encrypt("secret", "Hi Alice!");
console.log(sjcl.decrypt("secret", msg));

That was pretty simple. Let’s try something different, like hashing a string using SHA-256:

1
2
3
var hash = sjcl.hash.sha256.hash("hello world");
console.log("hash: "+sjcl.codec.hex.fromBits(hash));
// It should print: "hash: b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"

SJCL has many ADTs. In this case myhash is an array of 8 numbers (in js this is 64 bits floats) but it’s actually holding binary data (using just 32 bits for each number), so SJCL provides functions to convert, for example, from a binary array to a string in hexadecimal format.

For the next example, I would like to use some elliptic curve cryptography, but to do that we need to build SJCL first to enable this option:

1
2
3
4
$ git clone https://github.com/bitwiseshiftleft/sjcl.git sjcl
$ cd sjcl
$ ./configure --with-ecc
$ make

Now with our custom sjcl.js we can use the ECC primitives like DSA. So let’s sign the previous hash:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
// Generate a key pair and use it to sign the SHA-256 hash
var curve = sjcl.ecc.curves.k256;
var keys = sjcl.ecc.ecdsa.generateKeys(curve,6); // 6 is actually the default paranoia, so you can omit that
var signature = keys.sec.sign(hash);

// Extract the coordinates of the public point and create a public key object for testing
var pubkey_x = new curve.field(sjcl.bn.fromBits(keys.pub.get().x));
var pubkey_y = new curve.field(sjcl.bn.fromBits(keys.pub.get().y));
var point = new sjcl.ecc.point(curve, pubkey_x, pubkey_y);
var newpubkey = new sjcl.ecc.ecdsa.publicKey(curve, point);

// Print the signature and verify it
console.log("sign: "+sjcl.codec.base64.fromBits(signature));
console.log("verified: "+newpubkey.verify(hash, signature));

Obviously we didn’t need to create a new public key object to verify the signature, but I wanted to show how to obtain the public point. Finally, here is how we can serialize the public key (a point in the curve) and the private key (the exponent):

1
2
3
4
5
6
7
8
9
var pubkey_hex = sjcl.codec.hex.fromBits(keys.pub.get().x) + sjcl.codec.hex.fromBits(keys.pub.get().y);
var seckey_hex = sjcl.codec.hex.fromBits(keys.sec.get());

// Unserialize and create a public key object (SJCL will divide the binary array in two parts and get each coordinate)
var public_key = new sjcl.ecc.ecdsa.publicKey(curve, sjcl.codec.hex.toBits(pubkey_hex));

// Unserialize and create a private key object (we have to create a bignum object)
var secret_key_bn = new sjcl.bn(seckey_hex);
var secret_key = new sjcl.ecc.ecdsa.secretKey(curve, secret_key_bn);

There is a branch that already includes functions for serialization, so this method will be deprecated in the future.