Re: Comments on WebCryptoAPI

On Fri, Apr 25, 2014 at 3:44 PM, Ryan Sleevi <sleevi@google.com> wrote:

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

​As I have noted in the bug, this is a mis-understanding. There is no
"derive key" operation. The deriveBits and deriveKey methods both refer to
the "derive bits" operation. If an algorithm supports that it will work
with both ​deriveBits and deriveKey. This is because the difference between
those two methods is in whether the generated keying material is passed to
the import operation of the target *derived key algorithm*, or not. This
could be (generally is) a completely separate algorithm from the one doing
the deriving. So there is no point in distinguishing the two methods from
the derivation algorithm point of view.

I have proposed some time ago that we make this distinction between methods
and operations clearer (https://www.w3.org/Bugs/Public/show_bug.cgi?id=24827),
but so far noone has commented on this. It seems to be a repeated source of
confusion.

...Mark




>
>
>>
>> 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 23:19:39 UTC