slanted W3C logo
Cover page images (keys)

Re-igniting the Javascript Cryptography Wars

Harry Halpin, <hhalpin@w3.org&g t;



Providing secret cryptographic codes that the government couldn't spy on, was in fact a munition and this big war that we fought in the 1990s to try and make cryptography available to everyone, which we largely won, actually, which we largely won and it’s in every browser – now perhaps being backdoored and subverted in different kinds of ways...
Julian Assange

How the Web was Born

TimBL Meme

For more on these minimally constraining design guidelines that allowed the Web to spread the way it did: Architecture of the World Wide Web, Volume One
W3C Recommendation 15 December 2004

How the Web was Won

Too Many Standards

The Web Security Model (or Lack Thereof)

Too Many Standards

There are many things Web Crypto will not fix!

The Web Crypto API is not actually part of the Web Security Model. It adds new capacities to WebApps that were not (at all safely) available before.

Cryptography does not equal security, but is one part of an evolving eco-system. For examples of security beyond crypto, think traffic analysis!

Javascript Cryptography Considered Harmful - ORLY?

XML Crypto

Great blog post by Matasano Security.

What do you mean, "Javascript cryptography"? Secure delivery of Javascript to browsers is a chicken-egg problem: If you don't trust the network to deliver a password, or, worse, don't trust the server not to keep user secrets, you can't trust them to deliver security code...

WebCrypto API is cross-site scripting (XSS) and cross-site request forgery (CSRF) over HTTP is not going to be fixed by the Crypto API. We're going to have to assume that you do this right and take necessary precautions (CSP+upcoming CertTrans+HSTS is a start, albeit hard). Of course, use TLS 1.2 (see IETF RFC 6176

Browser Javascript is hostile to cryptography. The malleability of the Javascript runtime...The problem with running crypto code in Javascript is that practically any function that the crypto depends on could be overridden silently by any piece of content used to build the hosting page.

Again, we're going to assume you built your WebApp correctly. We realize this is hard. Is it impossible? To thwart a determined attacker, it very might be...but then they also might trojan your desktop machine as well or seize it.

What systems programming functionality does Javascript lack? A secure random number generator...secure erase (Javascript is usually garbage collected, so secrets are lurking in memory potentially long after they're needed) and functions with known timing characteristics... a secure keystore

Functionality for cryptography is the problem we are trying to solve! In particular, a secure (pseudo)random number generator and functions with known timing chraacteristics. Secure erase is something that we'll leave to the general design of JS at ECMA...

Check back in 10 years when the majority of people aren't running browsers from 2008.

The world is moving fast. Just because someone uses a system with a known security flaw does not mean we should not develop systems that address the flaw. The Web is only going to have as good security as folks like you put into writing, editing, and reviewing specifications and open-source code. Please help!

Javascript Cryptography: What can be Fixed!

Baby Meme

We agree its dangerous to roll your own crypto. Crypto.cat is a great project as its terrible to use OTR messaging (BTW, also ever look at the Pidgin code), yet we need to provide the primitives to let use-cases like multi-party OTR be done securely in Crypto.cat! Right now they have no choice but to use a plug-in.

In the words of a certain security hacker, We know the browser the good stuff in it - let me get at that!

Things we can do:

Plug-ins aren't the answer

Attacker Models and Web Apps

Baby Meme

Bruce Schneir states: Patrick Ball did a great job:

CryptoCat is one of a whole class of applications that rely on what's called "host-based security". The most famous tool in this group is Hushmail, an encrypted e-mail service that takes the same approach. Unfortunately, these tools are subject to a well-known attack. I'll detail it below, but the short version is if you use one of these applications, your security depends entirely the security of the host. This means that in practice, CryptoCat is no more secure than Yahoo chat, and Hushmail is no more secure than Gmail. More generally, your security in a host-based encryption system is no better than having no crypto at all.

The W3C Web Cryptography Use-Cases

Baby Meme

The W3C Web Cryptography Working Group

keygen2 meme

See homepage and charter.

Originally came out of an idea to improve authentication on identity on the Web at the public W3C Identity in the Browser Workshop in May 2011.

Three Deliverables (so far!):

Check them all out using the W3C Mercurial Repo

Luckily, the W3C Working Group does not have to write new cryptographic libraries, but rely on writing Javascript wrappers to expose routines from well-verified libraries built on top such as NSS (Chrome, Mozilla...) or OS-specific libraries (Microsoft Cryptographic API, Microsoft Next-Generation Cryptographic API)

Primary API Features in scope are:

Secondary API Features that may be in scope are:

Web Cryptography API

And the Crowd Goes Wild

XML Crypto

W3C Web Cryptography API Overview

Baby Meme

Privacy: Keys as Supercookies

Baby Meme
Fingerprinting
Malicious applications may be able to fingerprint users or user agents by detecting or enumerating the list of algorithms that are supported. This is especially true if an implementation exposes details about users' smart cards or secure element storage, as the combination of algorithms supported by such devices may be used to fingerprint devices more accurately than just the particular user agent.
Tracking
If user agents permit keys to be re-used between origins, without performing any secondary operations such as key derivation that includes the origin, then it may be possible for two origins to collude and track a unique user by recording their ability to access a common key.
Super-cookies
With the exception of ephemeral keys, its often desirable for applications to strongly associate users with keys. These associations may be used to enhance the security of authenticating to the application, such as using a key stored in a secure element as a second factor, or may be used by users to assert some identity, such as an e-mail signing identity. As such, these keys often live longer than their counterparts such as usernames and passwords, and it may be undesirable or prohibitive for users to revoke these keys. Because of this, keys may exist longer than the lifetime of the browsing context and beyond the lifetime of items such as cookies, thus presenting a risk that a user may be tracked even after clearing such data. This is especially true for keys that were pre-provisioned for particular origins and for which no user interaction was provided.

RandomSource

Javascript's native Math.random does not generate cryptographically strong pseudo-random numbers. This was proposed by Adam Barth to WHATWG, now going into WebCrypto API.

Baby Meme

[NoInterfaceObject]
interface RandomSource {
  ArrayBufferView getRandomValues(ArrayBufferView array);
};
        

The RandomSource interface represents an interface to a cryptographically strong pseudo-random number generator seeded with truly random values.

Implementations should generate cryptographically random values using well-established cryptographic pseudo-random number generators seeded with high-quality entropy, such as from an operating-system entropy source (e.g., "/dev/urandom"). Provides no lower-bound on the information theoretic entropy present in cryptographically random values, but implementations should make a best effort to provide as much entropy as practicable.

Historically, in each environment (window) Javascript is synchronous - which presents a huge problem for computationally expensive operations such as key generation, tackled by API by "Promises" style API design.

The WorkerCrypto interface provides cryptographic functionality for background scripts, as specified by Web Workers [Web Workers], and the general style of our API is made to allow asynchronous operations.

WorkerCrypto interface


interface WorkerCrypto {
};

WorkerCrypto implements RandomSource;

partial interface WorkerGlobalScope {
  readonly attribute WorkerCrypto crypto;
};
        

Key Interface

The Key object represents an opaque reference to keying material that is managed by the user agent.


enum KeyType {
  "secret",
  "public",
  "private"
};

enum KeyUsage {
  "encrypt",
  "decrypt",
  "sign",
  "verify",
  "derive"
};

interface Key {
  readonly attribute KeyType type;
  readonly attribute bool extractable;
  readonly attribute Algorithm algorithm;
  readonly attribute KeyUsage[] keyUsage;
};

type
The type of the underlying key. Opaque keying material, including that used for symmetric algorithms, is represented by "secret", while keys used as part of asymmetric algorithms composed of public/private keypairs will be either "public" or "private".
extractable
Whether or not the raw keying material may be exported by the application.
algorithm
The Algorithm used to generate the key.
keyUsage
An Array of KeyUsages that indicate what CryptoOperations may be used with this key.

Storing the Key (Red Alert!)

Private key material is stored using a "structured clone" algorithm (thus, in IndexedDB currently, although this may change in the future. This is due to privacy reasons, rather than the original plan of using an entirely different KeyStorage that would have a different lifespan than cookies etc.

When a user agent is required to obtain a structured clone of a Key object, it must run the following steps.

  1. Let input and memory be the corresponding inputs defined by the internal structured cloning algorithm, where input represents a Key object to be cloned.
  2. Let output be a newly constructed Key object.
  3. Let the following attributes of output be equal to the value obtained by invoking the internal structured clone algorithm recursively, using the corresponding attribute on input as the new "input" argument and memory as the new "memory" argument:
  4. Let output refer to the same underlying cryptographic material and cryptographic material key storage of input.

It is important that the underlying cryptographic key material not be exposed to a JavaScript implementation. Such a situation may arise if an implementation fails to implement the structured clone algorithm correctly, such as by allowing a Key object to be serialized as part of a structured clone implementation, but then deserializing it as a DOMString, rather than as a Key object.

OPEN-ISSUE: Keys can be unsafe when re-used of course, but detecting is hard for the WebApp developer. One proposed technical solution for user agents is to implement "key tainting", in which it records how a particular key has been used (eg: algorithms, parameters), and prevents it from being re-used in a manner that is unsafe or contrary to the security - such as preventing a PKCS1-v1.5 key from being used with RSA-PSS, or preventing an RSA-OAEP w/ MGF1-SHA1 from being used with RSA-OAEP w/ MGF1-SHA256. Questions exist about whether this should be encouraged or permitted, and the interoperability concerns it might cause.

Key Discovery and Naming

WebCrypto Key Discovery Draft split from main spec.

Necessary for out-of-band key provisioning with origin-specific keys.

 NamedKey : Key {
    readonly attribute DOMString  name;
    readonly attribute DOMString? id;
};

Attributes

id of type DOMString, readonly, nullable

A global identifier associated with the key. OPEN ISSUE

Origin-specific pre-provisioned keys are frequently provisioned with associated identifiers. Where an identifier exists that uniquely identifies the key amongst all keys pre-provisoned with the same origin and name and if this identifier can be canonically expressed as a sequence of no more than 256 bytes, then this identifier should be exposed, base64 encoded, as the id. If no identifier matching these conditions exists, id must be null.

name of type DOMString, readonly

A local identifier for the key.

Immutability of NamedKey objects

The name and id attributes of a NamedKey object shall not change. The underlying cryptographic key shall not change, except that it may be removed altogether. In this case any attempt to use the NamedKey object shall return an error.

CryptoKeys interface

[NoInterfaceObject]
interface CryptoKeys {
    KeyOperation getKeysByName (DOMString name);

Methods

GetKeysByName

When invoked, this method must perform the following steps:

  1. Let KeyOp be a newly created object implementing the KeyOperation interface
  2. Queue an operation to asynchronously run the following steps:
    1. Search for a key or keys matching the name specifier provided in name. A name specifier matches the name of a key if they are identical when expressed as a string of unicode characters.
      If one or more keys are found
      1. Let the result attribute of KeyOp be an object of type NamedKey[] containing the keys
      2. queue a task to fire a simple event called onsuccessat KeyOp
      Otherwise
      queue a task to fire a simple event called onerror at KeyOp
  3. Return KeyOp to the task that invoked getKeysByName

A name specifier matches the name of a key if they are identical when expressed as a string of unicode characters. If no matching keys are found, and empty array is returned.

CryptoOperations

Every CryptoOperation object must have a list of pending data. Each item in the list represents data that should be transformed by the cryptographic operation. The list functions as a queue that observes first-in, first-out ordering. That is, the order in which items are added shall reflect the order in which items are removed.

When a CryptoOperation is said to process data, the user agent must execute the following steps:

  1. If there are no items in the list of pending data, the algorithm is complete.

  2. Perform the underlying cryptographic algorithm, using bytes as the input data.

  3. If the cryptographic operation fails, proceed to the error steps below:

    1. Update the internal state to "error".

    2. Queue a task to fire a simple event named onerror at the CryptoOperation.

    3. Terminate the algorithm.

  4. Let output be the result of the underlying cryptographic algorithm.

OPEN ISSUE: There is an open question as to how the API should support key wrap and unwrap operations. Should they be distinct operations, independent from key import/export, or should they be part of the parameters supplied during import/export.

OPEN ISSUE: Further distinction is needed to clarify the differences between key generation and key derivation. Should they be distinguished by their inputs (Key generation takes parameters, while key derivation takes parameters + key(s)), by their outputs (Key generation generates Keys, key derivation generates opaque bytes as secret material), or is there some other construct to distinguish the two?

CryptoOperation Interface


enum KeyFormat {
  // An unformatted sequence of bytes. Intended for secret keys.
  "raw",
  // The DER encoding of the PrivateKeyInfo structure from RFC 5208.
  "pkcs8",
  // The DER encoding of the SubjectPublicKeyInfo structure from RFC 5280.
  "spki",
  // The key is represented as JSON according to the JSON Web Key format.
  "jwk",
};

interface Crypto {
  CryptoOperation encrypt(AlgorithmIdentifier algorithm,
                          Key key,
                          optional ArrayBufferView? buffer = null);
  CryptoOperation decrypt(AlgorithmIdentifier algorithm,
                          Key key,
                          optional ArrayBufferView? buffer = null);
  CryptoOperation sign(AlgorithmIdentifier algorithm,
                       Key key,
                       optional ArrayBufferView? buffer = null);
  CryptoOperation verify(AlgorithmIdentifier algorithm,
                         Key key,
                         ArrayBufferView signature,
                         optional ArrayBufferView? buffer = null);
  CryptoOperation digest(AlgorithmIdentifier algorithm,
                         optional ArrayBufferView? buffer = null);

  // TBD: ISSUE-36
  KeyOperation generateKey(AlgorithmIdentifier algorithm,
                           bool extractable = false,
                           KeyUsage[] keyUsages = []);
  KeyOperation deriveKey(AlgorithmIdentifier algorithm,
                         Key baseKey,
                         AlgorithmIdentifier? derivedKeyType,
                         bool extractable = false,
                         KeyUsage[] keyUsages = []);
  
  // TBD: ISSUE-35
  KeyOperation importKey(KeyFormat format,
                         ArrayBufferView keyData,
                         AlgorithmIdentifier? algorithm,
                         bool extractable = false,
                         KeyUsage[] keyUsages = []);
  KeyOperation exportKey(KeyFormat format, Key key);
};

Algorithm Dictionaries

The Algorithm object is a dictionary object which is used to specify an algorithm and any additional parameters required to fully specify the desired operation.


typedef (Algorithm or DOMString) AlgorithmIdentifier;

dictionary AlgorithmParameters {
};

dictionary Algorithm {
  DOMString name;
  AlgorithmParameters params;
};
        

OPEN ISSUE: Right now algorithm registration is done in the specification, but W3C generally does not run registires and Working Group has finite life span. Thus, move a registry to IANA?

OPEN ISSUE: Should algorithms permit short-names (string identifiers) as equivalent to specifying Algorithm dictionaries, or should Algorithm dictionaries be the only accepted form?

Each registered algorithm MUST have a canonical name for which applications can refer to the algorithm. The canonical name MUST contain only ASCII characters and MUST NOT equal any other canonical name or algorithm alias when every character in both names are converted to lower case.

Each registered algorithm MUST define the operations that it supports.

Each registered algorithm MUST define the expected contents of the params member of the Algorithm object for every supported operation.

Each registered algorithm MUST define the normalization rules for the contents of the params member of the Algorithm object for every supported operation.

Each registered algorithm MUST define the contents of the result attribute of the CryptoOperation object for every supported operation.

Recognized Algorithm Names

RSAES-PKCS1-v1_5

Encryption and decryption ordering to the RSAES-PKCS1-v1_5 algorithm specified in [RFC3447].


dictionary RsaSsaParams : AlgorithmParameters {
  // The hash algorithm to use 
  AlgorithmIdentifier hash;
};
          

RSA-PSS

Signing and verification using the RSASSA-PSS algorithm specified in [RFC3447].


dictionary RsaPssParams : AlgorithmParameters {
  // The hash function to apply to the message
  AlgorithmIdentifier hash;
  // The mask generation function
  AlgorithmIdentifier mgf;
  // The desired length of the random salt
  unsigned long saltLength;
};
            

RSA-OAEP

Encryption and decryption ordering to the RSAES-OAEP algorithm specified in [RFC3447].


dictionary RsaOaepParams : AlgorithmParameters {
  // The hash function to apply to the message
  AlgorithmIdentifier hash;
  // The mask generation function
  AlgorithmIdentifier mgf;
  // The optional label/application data to associate with the message
  ArrayBufferView? label;
};
            

ECDSA

Signing and verification using the ECDSA algorithm specified in [X9.62].


dictionary EcdsaParams : AlgorithmParameters {
  // The hash algorithm to use
  AlgorithmIdentifier hash;
};
            

Details on some named curves


enum NamedCurve {
  // NIST recommended curve P-256, also known as secp256r1.
  "P-256",
  // NIST recommended curve P-384, also known as secp384r1.
  "P-384",
  // NIST recommended curve P-521, also known as secp521r1.
  "P-521"

ECDH

Using Elliptic Curve Diffie-Hellman (ECDH) for key generation and key agreement, as specified by X9.63.


typedef Uint8Array ECPoint;

dictionary EcdhKeyDeriveParams : AlgorithmParameters {
  // The peer's EC public key.
  ECPoint public;
};
 

AES-CTR


dictionary AesCtrParams : AlgorithmParameters {
  // The initial value of the counter block. counter MUST be 16 bytes
  // (the AES block size). The counter bits are the rightmost length
  // bits of the counter block. The rest of the counter block is for
  // the nonce. The counter bits are incremented using the standard
  // incrementing function specified in NIST SP 800-38A Appendix B.1:
  // the counter bits are interpreted as a big-endian integer and
  // incremented by one.
  ArrayBuffer counter;
  // The length, in bits, of the rightmost part of the counter block
  // that is incremented.
  [EnforceRange] octet length;
};
            

AES-CBC


dictionary AesCbcParams : AlgorithmParameters {
  // The initialization vector. MUST be 16 bytes.
  ArrayBufferView iv;
};
            

AES-GCM


dictionary AesGcmParams : AlgorithmParameters {
  // The initialization vector to use. May be up to 2^56 bytes long.
  ArrayBufferView? iv;
  // The additional authentication data to include.
  ArrayBufferView? additionalData;
  // The desired length of the authentication tag. May be 0 - 128.
  [EnforceRange] octet? tagLength = 0;
};
            

HMAC


dictionary HmacParams : AlgorithmParameters {
  // The inner hash function to use.
  AlgorithmIdentifier hash;
};
            

DH

This describes using Diffie-Hellman for key generation and key agreement, as specified by PKCS #3.


dictionary DhKeyGenParams : AlgorithmParameters {
  // The prime p.
  BigInteger prime;
  // The base g.
  BigInteger generator;
};
            

SHA

As specified by [FIPS 180-4]

"SHA-1"
"SHA-224"
"SHA-256"
"SHA-384"
"SHA-512"

CONCAT

Key derivation algorithm defined in Section 5.8.1 of NIST SP 800-56A [SP800-56A].


dictionary ConcatParams : AlgorithmParameters {
  // The digest method to use to derive the keying material.
  AlgorithmIdentifier hash;

  // A bit string corresponding to the AlgorithmId field of the OtherInfo parameter.
  // The AlgorithmId indicates how the derived keying material will be parsed and for which
  // algorithm(s) the derived secret keying material will be used.
  ArrayBufferView algorithmId;

  // A bit string that corresponds to the PartyUInfo field of the OtherInfo parameter.
  ArrayBufferView partyUInfo;
  // A bit string that corresponds to the PartyVInfo field of the OtherInfo parameter.
  ArrayBufferView partyVInfo;
  // An optional bit string that corresponds to the SuppPubInfo field of the OtherInfo parameter.
  ArrayBufferView? publicInfo;
  // An optional bit string that corresponds to the SuppPrivInfo field of the OtherInfo parameter.
  ArrayBufferView? privateInfo;
};
            

PBKDF2


dictionary Pbkdf2Params : AlgorithmParameters {
  ArrayBufferView salt;
  [Clamp] unsigned long iterations;
  AlgorithmIdentifier prf;
  ArrayBufferView? password;
};
            

Example 1: Public Key Encryption

Generate a signing key pair, sign some data


// Algorithm Object
var algorithmKeyGen = {
  name: "RSASSA-PKCS1-v1_5",
  // RsaKeyGenParams
  params: {
    modulusLength: 2048,
    publicExponent: new Uint8Array([0x01, 0x00, 0x01]),  // Equivalent to 65537
  }
};

var algorithmSign = {
  name: "RSASSA-PKCS1-v1_5",
  // RsaSsaParams
  params: {
    hash: {
      name: "SHA-256",
    }
  }
};

var keyGen = window.crypto.generateKey(algorithmKeyGen,
                                       false, // extractable
                                       ["sign"]);

keyGen.oncomplete = function(event) {
  // Because we are not supplying data to .sign(), a multi-part
  // CryptoOperation will be returned, which requires us to call .process()
  // and .finish().
  var signer = window.crypt.sign(algorithmSign, event.target.result.privateKey);
  signer.oncomplete = function(event) {
    console.log("The signature is: " + event.target.result);
  }
  signer.onerror = function(event) {
    console.error("Unable to sign");
  }

  var dataPart1 = convertPlainTextToArrayBufferView("hello,");
  var dataPart2 = convertPlainTextToArrayBufferView(" world!");
  // TODO: create example utility function that converts text -> ArrayBufferView

  signer.process(dataPart1);
  signer.process(dataPart2);
  signer.finish();
};

keyGen.onerror = function(event) {
  console.error("Unable to generate a key.");
};
        

Example 2: Symmetric Encryption


var clearDataArrayBufferView = convertPlainTextToArrayBufferView("Plain Text Data");
// TODO: create example utility function that converts text -> ArrayBufferView

var aesAlgorithmKeyGen = {
  name: "AES-CBC",
  // AesKeyGenParams
  params: {
    length: 128
  }
};

var aesAlgorithmEncrypt = {
  name: "AES-CBC",
  // AesCbcParams
  params: {
    iv: window.crypto.getRandomValues(new Uint8Array(16))
  }
};

// Create a keygenerator to produce a one-time-use AES key to encrypt some data
var cryptoKeyGen = window.crypto.generateKey(aesAlgorithmKeyGen,
                                             false, // extractable
                                             ["encrypt"]);

cryptoKeyGen.oncomplete = function(event) {
  // A new, random AES key has been generated.
  var aesKey = event.target.result;

  // Unlike the signing example, which showed multi-part encryption, here we
  // will perform the entire AES operation in a single call.
  var aesOp = window.crypto.encrypt(aesAlgorithmEncrypt, aesKey, clearDataArrayBufferView);
  aesOp.oncomplete = function(event) {
    // The clearData has been encrypted.
    var ciphertext = event.target.result; // ArrayBufferView
  };
  aesOp.onerror = function(event) {
    console.error("Unable to AES encrypt.");
  };
};
        

The Future

Too Many Standards

To get updates on what standards need review:

Some things on the table...

With Great Power...

XML Crypto

.. comes great responsiblity

Cryptography can be used for many things:

Acknowledgements

standards expert meme

YOU for reviewing the specification!