Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Keeping Secrets with JavaScript - An Introduction to the WebCrypto API

Tim Taubert
September 13, 2014

Keeping Secrets with JavaScript - An Introduction to the WebCrypto API

With the web slowly maturing as a platform the demand for cryptography in the browser has risen, especially in a post-Snowden era. Many of us have heard about the upcoming Web Cryptography API but at the time of writing there seem to be no good introductions available. We will take a look at the proposed W3C spec and its current state of implementation.

Tim Taubert

September 13, 2014
Tweet

More Decks by Tim Taubert

Other Decks in Programming

Transcript

  1. Tim Taubert @ttaubert Keeping secrets with JavaScript An Introduction to

    the WebCrypto API September 2014, Berlin February 2015, Brussels
  2. // <script src="localforage.min.js"/> var NotesStorage = { load: function ()

    { return localforage.getItem("notes"); }, save: function (notes) { return localforage.setItem("notes", notes); } };
  3. // Compute a 256-bit digest. crypto.subtle.digest("SHA-256", data) .then(function (digest) {

    console.log(digest); }); // Output: ArrayBuffer {byteLength: 32} W3C API EXAMPLE https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  4. window.crypto.subtle “It is named SubtleCrypto to reflect the fact that

    many of these algorithms have subtle usage requirements in order to provide the required algorithmic security guarantees.”
  5. save: function (buffer) { // Compute the SHA-256 digest. return

    crypto.subtle.digest("SHA-256", buffer) .then(function (digest) { return Promise.all([ localforage.setItem("notes", buffer), localforage.setItem("notes_hash", digest) ]); }); }
  6. load: function () { var values = Promise.all([ localforage.getItem("notes"), localforage.getItem("notes_hash")

    ]); return values.then(function ([notes, notes_hash]) { // Compute the SHA-256 digest for |notes|. return crypto.subtle.digest("SHA-256", notes) .then(function (digest) { if (compare(notes_hash, digest)) { return notes; } }); }); }
  7. // Compute a 256-bit HMAC. crypto.subtle.sign("HMAC", key, data) .then(function (mac)

    { console.log(mac); }); // Output: ArrayBuffer // Verify an HMAC. crypto.subtle.verify("HMAC", key, mac, data) .then(function (valid) { console.log(valid); }); // Output: bool W3C API EXAMPLE https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  8. save: function (key, buffer) { // Generate a MAC using

    the given key. return crypto.subtle.sign("HMAC", key, buffer) .then(function (mac) { return Promise.all([ localforage.setItem("notes", buffer), localforage.setItem("notes_mac", mac) ]); }); }
  9. load: function (key) { var values = Promise.all([ localforage.getItem("notes"), localforage.getItem("notes_mac")

    ]); return values.then(function ([notes, notes_mac]) { // Verify the MAC using the given key. return crypto.subtle.verify("HMAC", key, notes_mac, notes) .then(function (valid) { if (valid) { return notes; } }); }); }
  10. // Derive a new key from a given key. crypto.subtle.deriveKey(algorithm,

    baseKey, derivedKeyType, extractable, keyUsages) .then(function (key) { console.log(key); }); // Output: CryptoKey object W3C API EXAMPLE https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  11. function deriveKey(pwKey, salt) { var params = { name: "PBKDF2",

    hash: "SHA-256", salt: salt, // The more iterations the slower, but also more secure. iterations: 5000 }; // The derived key will be used to compute HMACs. var alg = {name: "HMAC", hash: "SHA-256"}; var usages = ["sign", "verify"]; return crypto.subtle.deriveKey( params, pwKey, alg, false, usages); }
  12. function retrievePWKey() { // We will derive a new key

    from it. var usages = ["deriveKey"]; // Show a native password input dialog. return crypto.subtle.generateKey( "PBKDF2", false, usages); }
  13. function getSalt() { // Try to read a stored salt.

    return localforage.getItem("salt") .then(function (salt) { if (salt) { return salt; } // We should generate at least 8 bytes // to allow for 2^64 possible variations. var salt = crypto.getRandomValues(new Uint8Array(8)); return localforage.setItem("salt", salt); }); }
  14. var params = Promise.all([ // Get base key and salt.

    retrievePWKey(), getSalt() ]); var derivedKey = params.then(function ([pwKey, salt]) { // Do the PBKDF2 dance. return deriveKey(pwKey, salt); }); derivedKey.then(function (key) { // Store notes using the generated key. NotesStorage.save(key, buffer); });
  15. // Encrypt |data| under |key| using |alg|. crypto.subtle.encrypt(alg, key, data)

    .then(function (ct) { console.log(ct); }); // Output: ArrayBuffer // Decrypt |data| using |key| and algorithm |alg|. crypto.subtle.decrypt(alg, key, data) .then(function (pt) { console.log(pt); }); // Output: ArrayBuffer W3C API EXAMPLE https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html
  16. AES-GCM "Lorem ipsum dolor sit amet" 0xb5c73a8820a… (data) (ciphertext +

    mac) 0x1234567890a… (key) 0xd877fa3e919… (nonce)
  17. function deriveKey(pwKey, salt) { var params = { // …

    }; // The derived key will be used for AES. var alg = {name: "AES-GCM", length: 256}; var usages = ["encrypt", "decrypt"]; return crypto.subtle.deriveKey( params, pwKey, alg, false, usages); }
  18. save: function (key, buffer) { // Set up parameters. var

    nonce = crypto.getRandomValues(new Uint8Array(16)); var alg = {name: "AES-GCM", iv: nonce}; // Encrypt |buffer| under |key| using AES-GCM. return crypto.subtle.encrypt(alg, key, buffer) .then(function (notes_enc) { return Promise.all([ localforage.setItem("notes", notes_enc), localforage.setItem("nonce", nonce) ]); }); }
  19. load: function (key) { var values = Promise.all([ localforage.getItem("notes"), localforage.getItem("nonce")

    ]); return values.then(function ([enc_notes, nonce]) { var alg = {name: "AES-GCM", iv: nonce}; return crypto.subtle.decrypt(alg, key, enc_notes) .then(null, function (err) { // Verification failed. }); }); }
  20. COMPARE() SHA-256 RENDER (digest) DISCARD LOAD NOTES LOAD HASH EQUAL?

    (yes) (no) STORAGE SAFE AGAINST CORRUPTIONS
  21. HMAC-VERIFY RENDER DISCARD LOAD KEY LOAD MAC VALID? (yes) (no)

    STORAGE SAFE AGAINST TAMPERING LOAD NOTES
  22. HMAC-VERIFY RENDER DISCARD LOAD SALT LOAD MAC VALID? (yes) (no)

    STORAGE SAFE AGAINST TAMPERING PROTECTED BY A USER-GIVEN PASSWORD LOAD NOTES PBKDF2 (derived key) PW PROMPT
  23. AES-GCM RENDER DISCARD LOAD SALT LOAD NONCE VALID? (yes) (no)

    STORAGE SAFELY ENCRYPTED BY A USER-GIVEN PASSWORD LOAD NOTES PBKDF2 (derived key) PW PROMPT
  24. @dreid 20 years of abstinence-only cryptography education hasn't gotten us

    anything but an endless supply of bad crypto in production systems.
  25. Stanford - Cryptography I + II by Dan Boneh https://www.coursera.org/course/crypto

    Introduction to Cryptography by Christof Paar https://www.youtube.com/channel/UC1usFRN4LCMcfIV7UjHNuQg/videos LEARNING RESOURCES