$30 off During Our Annual Pro Sale. View Details »

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

    View Slide

  2. WHY A CRYPTO API?

    View Slide

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

    View Slide

  4. //
    var NotesStorage = {
    load: function () {
    return localforage.getItem("notes");
    },
    save: function (notes) {
    return localforage.setItem("notes", notes);
    }
    };

    View Slide

  5. NotesStorage.save([
    {title: "Return $200 to Alice"},
    {title: "Buy soy milk", due: "2014-09-15"}
    ]);

    View Slide

  6. INTEGRITY

    View Slide

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

    View Slide

  8. SHA-256
    CRYPTOGRAPHIC HASH FUNCTION

    View Slide

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

    View Slide

  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

    View Slide

  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.”

    View Slide

  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)
    ]);
    });
    }

    View Slide

  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;
    }
    });
    });
    }

    View Slide

  14. INTEGRITY &
    AUTHENTICITY

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  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)
    ]);
    });
    }

    View Slide

  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;
    }
    });
    });
    }

    View Slide

  20. PASSWORDS ARE NOT
    CRYPTOGRAPHIC KEYS

    View Slide

  21. KEY DERIVATION

    View Slide

  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

    View Slide

  23. PBKDF2
    PASSWORD-BASED KEY DERIVATION

    View Slide

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

    View Slide

  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);
    }

    View Slide

  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);
    }

    View Slide

  27. View Slide

  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);
    });
    }

    View Slide

  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);
    });

    View Slide

  30. INTEGRITY,
    AUTHENTICITY &
    SECRECY

    View Slide

  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

    View Slide

  32. AES-GCM
    ADVANCED ENCRYPTION STANDARD
    IN GALOIS/COUNTER MODE

    View Slide

  33. AES-GCM
    "Lorem ipsum
    dolor sit amet" 0xb5c73a8820a…
    (data)
    (ciphertext + mac)
    0x1234567890a…
    (key)
    0xd877fa3e919…
    (nonce)

    View Slide

  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);
    }

    View Slide

  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)
    ]);
    });
    }

    View Slide

  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.
    });
    });
    }

    View Slide

  37. \o/
    https://github.com/ttaubert/secret-notes

    View Slide

  38. RECAP

    View Slide

  39. COMPARE()
    SHA-256
    RENDER
    (digest)
    DISCARD
    LOAD NOTES LOAD HASH
    EQUAL?
    (yes)
    (no)
    STORAGE SAFE AGAINST CORRUPTIONS

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  43. @dreid
    20 years of abstinence-only cryptography education
    hasn't gotten us anything but an endless supply of
    bad crypto in production systems.

    View Slide

  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

    View Slide

  45. THANKS
    @ttaubert
    [email protected]
    https://timtaubert.de/talks/keeping-secrets-with-javascript/

    View Slide