Keeping Secrets with JavaScript - An Introduction to the WebCrypto API

6bab41d9b9453e984752a40a0dccbffc?s=47 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.

6bab41d9b9453e984752a40a0dccbffc?s=128

Tim Taubert

September 13, 2014
Tweet

Transcript

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

    the WebCrypto API September 2014, Berlin February 2015, Brussels
  2. WHY A CRYPTO API?

  3. NOW WITH 100% MORE CRYPTO YET ANOTHER NOTES APP

  4. // <script src="localforage.min.js"/> var NotesStorage = { load: function ()

    { return localforage.getItem("notes"); }, save: function (notes) { return localforage.setItem("notes", notes); } };
  5. NotesStorage.save([ {title: "Return $200 to Alice"}, {title: "Buy soy milk",

    due: "2014-09-15"} ]);
  6. INTEGRITY

  7. > sha256sum ubuntu-14.10-desktop-amd64.iso 22ee4a41c010a027c7d298e3c985e9e5 \ 68c2a743ee0b0f97f0806285f44babb1

  8. SHA-256 CRYPTOGRAPHIC HASH FUNCTION

  9. SHA-256 "Lorem ipsum dolor sit amet" 0x8a995db78d0… (data) (digest)

  10. // 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
  11. 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.”
  12. 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) ]); }); }
  13. 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; } }); }); }
  14. INTEGRITY & AUTHENTICITY

  15. HMAC-SHA-256 HASH-BASED MESSAGE AUTHENTICATION CODE

  16. HMAC "Lorem ipsum dolor sit amet" 0x8a995db78d0… (data) (mac) 0x1234567890a…

    (key)
  17. // 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
  18. 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) ]); }); }
  19. 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; } }); }); }
  20. PASSWORDS ARE NOT CRYPTOGRAPHIC KEYS

  21. KEY DERIVATION

  22. // 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
  23. PBKDF2 PASSWORD-BASED KEY DERIVATION

  24. PBKDF2 "rbW-fk8;#9" 0x8a995db78d0… (password) (key) 0x572bf6e4ef8a… (salt) 5000 (iterations)

  25. 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); }
  26. 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); }
  27. None
  28. 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); }); }
  29. 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); });
  30. INTEGRITY, AUTHENTICITY & SECRECY

  31. // 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
  32. AES-GCM ADVANCED ENCRYPTION STANDARD IN GALOIS/COUNTER MODE

  33. AES-GCM "Lorem ipsum dolor sit amet" 0xb5c73a8820a… (data) (ciphertext +

    mac) 0x1234567890a… (key) 0xd877fa3e919… (nonce)
  34. 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); }
  35. 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) ]); }); }
  36. 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. }); }); }
  37. \o/ https://github.com/ttaubert/secret-notes

  38. RECAP

  39. COMPARE() SHA-256 RENDER (digest) DISCARD LOAD NOTES LOAD HASH EQUAL?

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

    STORAGE SAFE AGAINST TAMPERING LOAD NOTES
  41. 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
  42. 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
  43. @dreid 20 years of abstinence-only cryptography education hasn't gotten us

    anything but an endless supply of bad crypto in production systems.
  44. 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
  45. THANKS @ttaubert tim@timtaubert.de https://timtaubert.de/talks/keeping-secrets-with-javascript/