Re: JWK import/export as ECMAScript objects, rather than ArrayBuffer

Sent from my iPhone

On Mar 21, 2014, at 12:41 PM, Richard Barnes <rlb@ipv.sx> wrote:

On Fri, Mar 21, 2014 at 1:17 PM, Mark Watson <watsonm@netflix.com> wrote:

>
>
>
> On Fri, Mar 21, 2014 at 7:50 AM, Richard Barnes <rlb@ipv.sx> wrote:
>
>> On Tue, Mar 11, 2014 at 9:08 PM, Mark Watson <watsonm@netflix.com> wrote:
>>
>>>
>>>
>>>
>>> On Tue, Mar 11, 2014 at 6:00 PM, Ryan Sleevi <sleevi@google.com> wrote:
>>>
>>>>
>>>> On Mar 11, 2014 5:44 PM, "Mark Watson" <watsonm@netflix.com> wrote:
>>>> >
>>>> > Hi Ryan,
>>>> >
>>>> > This looks good with the following comments:
>>>> >
>>>> > (1) Why not add this capability, whilst keeping the existing JWK
>>>> import / export ? For example add a new format "jwk-obj". This would keep
>>>> things simple for the other use-case where the JWK is just being written to
>>>> / read from a wire protocol. Supporting both is hardly arduous since UAs
>>>> must support the serialization / de-serialization for wrap / unwrap anyway.
>>>> >
>>>>
>>>> We should prioritize the common case. Dealing with ArrayBuffers for
>>>> JSON or JS Objects is not the common case for any other Web API.
>>>>
>>> We might disagree about which is the common case. Since we already have
>>> the actual JWKs (and several browsers have implemented these and they are
>>> being used) it seems sensible to add functionality here. There is no
>>> additional work to add rather than replace, so there is no need to
>>> prioritize.
>>>
>>
>> It seems abundantly silly to have two options here, when there's a
>> trivial conversion between them.
>>
>> Object -> json.stringify() -> TextEncoder -> ABV
>> ABV -> TextDecoder -> json.parse() -> Object
>>
>> Whichever one we choose here, a small JS shim can emulate the other.  So
>> we should really just choose one.
>>
>
> This is fine if you have TextEncoder. It doesn't appear on caniuse.com.
> Does anyone have information about browser support of this ?
>

Even if you don't have TextEncoder, it's a small polyfill.  See, e.g.:
http://www.onicos.com/staff/iz/amuse/javascript/expert/utf.txt
http://polycrypt.net/common/util.js

Object -> json.stringify() -> utf16to8() -> somethingLike_str2abv() --> ABV
ABV -> something_like_abv2str() -> utf8to16() -> json.parse() --> Object


How efficient is the polyfill for large messages ?

...Mark


--Richard



>
> ...Mark
>
>
>
>>
>> As to which we should choose, I'm pretty strongly with Ryan here that we
>> should return an object.  ECMAScript objects are what get returned from any
>> other web API that returns a structured object.  The only exceptions we see
>> to this are when some binary format is used for backward compatibility;
>> that applies for "spki", "pkcs8", but not to "jwk".
>>
>> As far as implementation, I would be pretty shocked to see
>> implementations making these object in any way other than (1) building and
>> object and (2) calling json.stringify().  So from an implementation
>> complexity point of view, the only change here is to remove the call to
>> json.stringify().
>>
>>  If we support parallel formats, it should be because we believe the
>>>> ArrayBuffer case will be as significant a use case as with Object.
>>>>
>>> No, it's sufficient that they are both significant and useful. They
>>> don't need parity.
>>>
>>>>  In either event, if we do support both, I believe the ArrayBuffer case
>>>> should be separately named (eg: JWKS) - precisely because JWK will
>>>> naturally yield or be manipulated as Objects, not octets, within JS.
>>>>
>>> The J in JWK stands for JSON. JSON is a wire format. Seems obvious that
>>> format "jwk" should produce a JWK, not a Javascript representation of one.
>>>
>>>> You keep saying "written to or read from a wire protocol", so I'd
>>>> simply ask that you back that assertion up with script demonstrating this
>>>> use case.
>>>>
>>> This is what our application does today. I'll see if I can extract the
>>> relevant code.
>>>
>>
>> As noted above, even if your application does this, it's a small shim to
>> make an object-based API look like an ArrayBuffer-based one.
>>
>> I would also point out that there are potentially pretty big down-sides
>> to providing a pre-serialized JWK, in contexts where that JWK fits into a
>> larger protocol.  For example, if the overall protocols uses UTF-16 (e.g.,
>> based on sending ECMAScript strings without re-encoding), then the
>> pre-serialized JWK can't just be pasted into the protocol, since it's in
>> UTF-8.  But there are also enough protocols that use UTF-8 that we can't
>> just switch to UTF-16.  Better to just give the application the object and
>> let it serialize it into whatever form works.
>>
>> --Richard
>>
>>
>>
>>
>>>
>>> ...Mark
>>>
>>>> The most likely candidates - XHR and WebSockets - are naturally
>>>> inclined towards JSON and DOMString-ified JSON. Equally true for structured
>>>> inter-origin postMessage (which may not be suitable as a Key object if the
>>>> source is replaying a message from the server to the peer). These are all
>>>> cases served by a natural object - especially when it reduces repeated
>>>> conversions / copies.
>>>>
>>>> Equally, if we believe that really, the use case is only for importing
>>>> an ArrayBuffer, we could let the "jwk" importKey handle both cases
>>>> transparently, while always yielding an object for export key.
>>>>
>>>> TL:DR; What is a precise use case or demonstrable snippet where
>>>> ArrayBuffer would be more preferable or natural compared to an Object?
>>>>
>>>> > (2) WebIDL appears to be a little ambiguous with respect to unions of
>>>> Dictionaries. Section 14.2.25 says "If types includes a dictionary type,
>>>> then return the result of converting V to that dictionary type.
>>>> > ", which pointedly neglects to say which dictionary type in types, if
>>>> there is more than one.
>>>> >
>>>>
>>>> Sure. We could just as well deal with it as we deal with algorithm -
>>>> object and coercion.
>>>>
>>>> I agree this has to be solved meaningfully before adding to the spec.
>>>>
>>>> > (3) We need a
>>>> > JwkKeyDictionary subclass for symmetric keys:
>>>> >
>>>> > JwkSecretKeyDictionary : JwkKeyDictionary {
>>>> >   DOMString k;
>>>> > };
>>>> >
>>>> > (4) usages in JwkKeyDictionary should be "key_ops"
>>>> >
>>>> > (5) Do we want to include the "use" member ? I thought we checked
>>>> that for consistency ?
>>>> >
>>>>
>>>> All oversights of mine, as a result of basing on JWA without reading
>>>> all of our additions/registrations.
>>>>
>>>> Answer is yes, yes, and yes.
>>>>
>>>> > ...Mark
>>>> >
>>>> >
>>>> > On Tue, Mar 11, 2014 at 5:22 PM, Ryan Sleevi <sleevi@google.com>
>>>> wrote:
>>>> >>
>>>> >> The motivation:
>>>> >>
>>>> >> * Provide a means of easy interchange with Web Sockets (eg: by
>>>> allowing Text Frame, rather than imposing Binary Frame, as done by
>>>> send(ArrayBuffer) )
>>>> >> * Provide a means of easy interchange of JWK with postMessage (eg:
>>>> by not requiring a Key object itself be posted)
>>>> >> * Provide a means of easy integration with larger JSON-backed
>>>> messages
>>>> >>
>>>> >> That is, presume a structure
>>>> >> {
>>>> >>   'larger_message': 'something',
>>>> >>   'jwk': [{
>>>> >>     'kid': 'foo',
>>>> >>     'alg': 'RSA',
>>>> >>     'kty': 'RSA1_5',
>>>> >>     'n': '....',
>>>> >>     'e': '....'
>>>> >>   }]
>>>> >> }
>>>> >>
>>>> >> Under the current API, one has two options - depending on UA support
>>>> for http://encoding.spec.whatwg.org/#api
>>>> >>
>>>> >> With Encoding support:
>>>> >>
>>>> >> // Makes 3 additional copies of message.jwk
>>>> >> // 1 for the .stringify
>>>> >> // 1 for the TextEncoder
>>>> >> // 1 for the importKey (cloning the ArrayBuffer)
>>>> >> // By definition, this copies *all* fields of message.jwk, including
>>>> those not used by importKey (eg: 'kid')
>>>> >>
>>>> >> message = JSON.parse(message);
>>>> >> jwkBuf = (new
>>>> TextEncoder("utf-8")).encode(JSON.stringify(message.jwk));
>>>> >> window.crypto.subtle.importKey("jwk", jwkBuf, { name:
>>>> "RSAES-PKCS1-v1_5" }, [ "encrypt", "decrypt"] );
>>>> >>
>>>> >> Without Encoding support:
>>>> >> It's necessary to do something like strToUTF8Arr (
>>>> https://developer.mozilla.org/en-US/docs/Web/JavaScript/Base64_encoding_and_decoding)
>>>> >>
>>>> >> Under the proposed API, one simply does
>>>> >>
>>>> >> // Makes 1 additional copy of message.jwk
>>>> >> // 1 for the importKey (cloning message.jwk)
>>>> >> // By definition, this *does not* copy all fields of message.jwk -
>>>> only those fields used for the import (eg: 'kid' is NOT copied)
>>>> >> window.crypto.subtle.importKey("jwk", message.jwk, { name:
>>>> "RSAES-PKCS1-v1_5" }, [ "encrypt", "decrypt" ]);
>>>> >>
>>>> >>
>>>> >> Types used:
>>>> >>
>>>> >> dictionary JwkKeyDictionary {
>>>> >>   DOMString kty;
>>>> >>   DOMString alg;
>>>> >>   boolean ext;
>>>> >>   DOMString[] usages;
>>>> >> };
>>>> >>
>>>> >> dictionary JwkEcKeyDictionary : JwkKeyDictionary {
>>>> >>   DOMString crv;
>>>> >>   DOMString x;
>>>> >>   DOMString y;
>>>> >>   DOMString d;
>>>> >> };
>>>> >>
>>>> >> dictionary JwkRsaOtherPrimeDictionary {
>>>> >>   DOMString r;
>>>> >>   DOMString d;
>>>> >>   DOMString t;
>>>> >> };
>>>> >>
>>>> >> dictionary JwkRsaKeyDictionary : JwkKeyDictionary {
>>>> >>   DOMString n;
>>>> >>   DOMString e;
>>>> >>   DOMString d;
>>>> >>   DOMString p;
>>>> >>   DOMString dp;
>>>> >>   DOMString dq;
>>>> >>   DOMString qi;
>>>> >>   JwkRsaOtherPrimeDictionary[] oth;
>>>> >> };
>>>> >>
>>>> >>
>>>> >>
>>>> >> Changes to signatures:
>>>> >> Old:
>>>> >> Promise<any> importKey(KeyFormat format, CryptoOperationData
>>>> keyData, AlgorithmIdentifier? algorithm, boolean extractable, KeyUsage[]
>>>> keyUsages);
>>>> >> Promise<any> exportKey(KeyFormat format, Key key);
>>>> >>
>>>> >> New:
>>>> >> Promise<any> importKey(KeyFormat format, (CryptoOperationData or
>>>> JwkRsaKeyDictionary or JwkEcKeyDictionary), AlgorithmIdentifier algorithm,
>>>> boolean extractable, KeyUsage[] keyUsages);
>>>> >> Promise<any> exportKey(KeyFormat format, Key key);
>>>> >>
>>>> >>
>>>> >> Changes to algorithms:
>>>> >> Wrap Key ( 14.3.1 /
>>>> https://dvcs.w3.org/hg/webcrypto-api/raw-file/3f7df730b2c7/spec/Overview.html#SubtleCrypto-method-wrapKey)
>>>> >>
>>>> >> 12.
>>>> >> * If format is "spki":
>>>> >>   - Let bytes be the result of performing the export key operation
>>>> specified the algorithm attribute of key using key and format.
>>>> >> * If format is "pkcs8"
>>>> >>   - Let bytes be the result of performing the export key operation
>>>> specified the algorithm attribute of key using key and format.
>>>> >> * If format is "jwk"
>>>> >>   - Let object be the result of performing the export key operation
>>>> specified by the algorithm attribute of key using key and format.
>>>> >>   - Let stringifiedJwk be the result of invoking the JSON.stringify
>>>> method specified in Section 15.12.3 of [ECMA-252], with /object/ as /value/.
>>>> >>   - Let bytes be the UTF-8 encoding of stringifiedJwk
>>>> >>
>>>> >>
>>>> >> From the algorithm-specific import key sections eg: using
>>>> https://dvcs.w3.org/hg/webcrypto-api/raw-file/3f7df730b2c7/spec/Overview.html#rsassa-pkcs1-operationsas an example
>>>> >>
>>>> >> 4. If format is "jwk"
>>>> >>   1. If /keyData/ is not an instance of a JwkRsaKeyDictionary,
>>>> return an error ...
>>>> >>   2. Let /jwk/ be /keyData/
>>>> >>
>>>> >> From the algorithm-specific export key sections - eg: using again
>>>> RSASSA-PKCS1
>>>> >>
>>>> >> 4. If format is "jwk"
>>>> >>   * Let /jwk/ be a new ECMAScript object created as if by the
>>>> expression ({})
>>>> >>   * _Set the property "n" of /jwk/_ to the _base64url-encoded_
>>>> modulus of the RSA public key represented by /key/, as specified by Section
>>>> 6.3.1 of [JWA]
>>>> >>   * _Set the property "e" of /jwk/_ to the _base64url-encoded_ big
>>>> integer exponent representation of the RSA public key represented by /key/,
>>>> as specified by Section 6.3.1 of [JWA]
>>>> >>   ...
>>>> >>   * Let /result/ be /jwk/
>>>> >>
>>>> >> Terminology:
>>>> >> When this specification says Set the property /name/ of /object/ to
>>>> /value/, call the [[DefineOwnProperty]] internal method of /object/ with
>>>> property name /name/, the Property Descriptor { [[Writable]]: true,
>>>> [[Enumerable]]: true, [[Configurable]]: true, [[Value]]: /value/ }, and the
>>>> Boolean flag false.
>>>> >>
>>>> >>
>>>> >>
>>>> >
>>>> >
>>>>
>>>
>>>
>>
>

Received on Friday, 21 March 2014 20:00:19 UTC