Re: Comments on WebCryptoAPI

On Thu, Apr 24, 2014 at 1:38 PM, Matt Miller <mamille2@cisco.com> wrote:

> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA512
>
> Hello all,
>
> Below are my comments after reading the current draft of WebCryptoAPI.
>  I apologize if I've repeated anything stated before; I've not really
> followed the discussion leading up to this document's current state.
>
>
>
> Potential Defects:
> ==============================================
>
> The definition for "PBKDF2" includes an editor note that does not
> appear to be relevant.  It discusses a "password" field that is not
> present in any of the immediately surrounding content.
>
>
Thanks for highlighting. Will fix.


>
> Comments:
> ==============================================
>
> Key Storage/Retrieval
> - ----------------------------------------------
>
> The draft discusses "retrieving keys" -- distinct from import/unwrap
> - -- but does not in any other way indicate how this is dealt with.  It
> would help to explicitly mention that key storage/retrieval can be
> done with mechanisms that support structured cloning, such as IndexedDB.
>
>
Is there a particular section you have a concern of? The only mention of
this is in Section 4.4 of the "out of scope".


>
> KeyPairs and Extractability
> - ----------------------------------------------
>
> Assuming that when generateKey returns a KeyPair that the public key
> is always extractable, this should be explicitly stated somewhere
> separate from the individually registered algorithms.  At a minimum,
> this ought to be stated in § 18.3. "Defining an Algorithm", but it
> could also be helpful for have it stated at or around § 14.3.6. "The
> generateKey method" from the perspective of an API user.
>
>
I don't agree with this. I've tried very hard to specifically avoid listing
algorithm-specific concerns in generic sections.


>
> "Defining an algorithm" Guidelines
> - ----------------------------------------------
>
> If the to-be-defined algorithm supports the "importKey" operation, the
> definition ought to clearly specify how [extractable] applies.


> Examples
> - ----------------------------------------------
>
> It would be nice to a somewhat non-trivial deriveKey example.  For my
> own edification, I put together a mythical example (included below) to
> see if I'm understanding the API interactions correctly.  And, please,
> correct my misconceptions of how the API works if my example has
> egregious  errors.
>
> Key Lifetimes
> - ----------------------------------------------
>
> I suspect this is controversial.  It would be nice if a generated Key
> could have some sort of lifetime that the user agent enforces.  At a
> minimum, I would highly desire the ability to specify that a Key
> should not be used for more than a browser session (similar to
> sessionStorage), but would value having a fixed expiration date/time
> or a certain amount of time after generateKey's promise is fulfilled.
>
> I realize that this can complicate things (e.g., how does this
> interact with importKey?), and can live with some restrictions (e.g.,
> limited-life Keys are never extractable).  I think it's also
> acceptable for the lifetime to be tied to just the private Key of a
> KeyPair, if it helps simplify things at all.
>
>
Yeah, controversial. WebCrypto provides no statements about all about Key
Storage - this is very important, least of all for its security concerns.

You already have the ability to specify that a Key should not be used more
than a browser session - don't persist it anywhere!

I disagree with the premise that there's any security benefits to be had
here - it's fully in the control of the site author to control the lifetime
of their key, with either method. The proposal you make provides some
syntactic sugar, but notably complicates things - including forcing
WebCrypto to make explicit statements about Key Storage that simply cannot
or should not be made.


>
> Thank you,
>
> - - m&m
>
> Matt Miller < mamille2@cisco.com >
> Cisco Systems, Inc.
>
> - -----BEGIN EXAMPLE-----
>
> // Agree to a session key with another user
> var promise,
>     us = {},
>     them = {},
>     sessionKey = null;
>
> // helper for prefixing data with a length encoding
> function __UINT32(value) {
>     var output = new Uint8Array([
>         (value >>> 24) & 0xff,
>         (value >>> 16) & 0xff,
>         (value >>> 8) & 0xff,
>         value & 0xff
>     ]);
> }
>
> // Generate an EC Key for ECDH
> var params = {
>     name: "ECDH",
>     namedCurve: "P-256"
> };
> promise = window.crypto.subtle.generateKey(params,
>                                            false,
>                                            [ "deriveKey" ]);
> promise = promise.then(function (keypair) {
>     // remember the generated key for right now
>     us.pivateKey = keypair.privateKey;
>     us.publicKey = keypair.publicKey;
>
>     // set our identifier
>     var ident = new TextEncoding("utf-8").encode("me@example.com");
>     us.info = new Uint8Array(4 + ident.byteLength);
>     us.info.set(__UINT32(ident.byteLength));
>     us.info.set(ident, 4);
>
>     // export public key as JWK
>     return window.crypto.subtle.exportKey("jwk", us.publicKey);
> });
>
> promise = promise.then(function(jwk) {
>     // exchange public keys with 'them';
>     // returns a promise with the other user's public key,
>     // it could use XMLHttpRequest or WebSocket for the actual exchnage
>     return exchangePeerInfo({
>         publicKey: jwk,
>         info: us.info
>     });
> });
>
> promise = promise.then(function (other) {
>     them = other;
>
>     // import the key
>     return window.crypto.subtle.importKey("jwk",
>                                           them.key,
>                                           { name: "ECDH" },
>                                           true,
>                                           [ "deriveBits" ]);
> });
>
> promise = promise.then(function (theirKey) {
>     // remember their Key object
>     them.publicKey = theirKey;
>
>     // generate the shared secret "Key"
>     var params = {
>         name: "ECDH",
>         public: their.publicKey
>     };
>
>     return window.crypto.subtle.deriveKey(params,
>                                           us.privateKey,
>                                           { name: "CONCAT" },
>                                           false,
>                                           [ "deriveKey" ]);
> });
>

Concat only supports deriveBits, as currently specified.

Filed https://www.w3.org/Bugs/Public/show_bug.cgi?id=25468


>
> promise = promise.then(function (secret) {
>     // construct the algorithmId UINT32(algorithm.length) || algorithm
>     var algName = new TextEncoder("utf-8").encode("A128GCM");
>     var algId = new Uint8Array(4 + algName.byteLength);
>     algId.set(__UINT32(algName.byteLength));
>     algId.set(algName, 4);
>
>     var params = {
>         name: "CONCAT",
>         hash: "SHA-256",
>         algorithmsId: algId,
>         partyUInfo: us.info,
>         partyVInfo: them.info
>     };
>
>     var derived = {
>         name: "AES-GCM",
>         length: 128
>     };
>     return window.crypto.subtle.deriveKey(params,
>                                           secret,
>                                           derived,
>                                           false,
>                                           [ "encrypt", "decrypt" ]);
> });
>
>
The above certainly strikes me as somewhat dangerous (re-using the same key
for encryption and decryption). I would presume you would be generating
separate keys here - an encryption key that uses { partyUInfo: us.info,
partyVInfo: them.info }, and a decryption key that uses { partyUInfo:
them.info, partyVInfo: us.info }

Otherwise, yes, that looks correct.

Received on Friday, 25 April 2014 22:45:28 UTC