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
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
Copyright © 2005-2010 W3C ® (MIT, ERCIM, Keio), All Rights Reserved.
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!
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 keystoreFunctionality 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!
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:
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.
Multi-channel WebApp communication across multiple origins as a loosely-coupled design architecture should allow both authentication and even integrity.
Very useful for authentication across multiple hosts (like signing OAuth2 tokens to verify the client rather than using symmetric shared secrets). Think cookie-snatching. Now think cookie-snatching with verifiable signatures!
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
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.
[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.
interface WorkerCrypto {
};
WorkerCrypto implements RandomSource;
partial interface WorkerGlobalScope {
readonly attribute WorkerCrypto crypto;
};
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
"secret"
, while keys used as
part of asymmetric algorithms composed of public/private keypairs will be either
"public"
or "private"
.
extractable
algorithm
Algorithm
used to generate the key.
keyUsage
Array
of KeyUsages
that
indicate what CryptoOperations may be used with this
key.
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.
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.
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;
};
id
of type DOMString, readonly, nullableA 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, readonlyA local identifier for the key.
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.
[NoInterfaceObject]
interface CryptoKeys {
KeyOperation getKeysByName (DOMString name);
GetKeysByName
When invoked, this method must perform the following steps:
KeyOperation
interfacename
. A name specifier matches the name of a key if they are identical when expressed as a string of unicode characters.
result
attribute of KeyOp be an object of type NamedKey[]
containing the keysonsuccess
at KeyOponerror
at KeyOpA 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.
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:
If there are no items in the list of pending data, the algorithm is complete.
Perform the underlying cryptographic algorithm, using bytes as the input data.
If the cryptographic operation fails, proceed to the error steps below:
Update the internal state to "error"
.
Queue a task to
fire a simple event named
onerror
at the
CryptoOperation
.
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?
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);
};
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.
Encryption and decryption ordering to the RSAES-PKCS1-v1_5 algorithm specified in [RFC3447].
dictionary RsaSsaParams : AlgorithmParameters {
// The hash algorithm to use
AlgorithmIdentifier hash;
};
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;
};
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;
};
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"
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;
};
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;
};
dictionary AesCbcParams : AlgorithmParameters {
// The initialization vector. MUST be 16 bytes.
ArrayBufferView iv;
};
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;
};
dictionary HmacParams : AlgorithmParameters {
// The inner hash function to use.
AlgorithmIdentifier hash;
};
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;
};
As specified by [FIPS 180-4]
"SHA-1"
"SHA-224"
"SHA-256"
"SHA-384"
"SHA-512"
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;
};
dictionary Pbkdf2Params : AlgorithmParameters {
ArrayBufferView salt;
[Clamp] unsigned long iterations;
AlgorithmIdentifier prf;
ArrayBufferView? password;
};
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.");
};
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.");
};
};
To get updates on what standards need review:
Some things on the table...
.. comes great responsiblity
Cryptography can be used for many things:
Defending free expression
Protecting Unauthorized Access to Content
Take-Home Lessons
YOU for reviewing the specification!