- From: Jim Schaad <ietf@augustcellars.com>
- Date: Fri, 19 Jul 2013 11:40:19 -0700
- To: "'Ryan Sleevi'" <sleevi@google.com>, "'Richard Barnes'" <rbarnes@bbn.com>
- Cc: <public-webcrypto@w3.org>
> -----Original Message----- > From: Ryan Sleevi [mailto:sleevi@google.com] > Sent: Thursday, July 18, 2013 6:28 PM > To: Richard Barnes > Cc: public-webcrypto@w3.org Group > Subject: Re: ISSUE-12 / ACTION-83: Operation vs. Algorithm parameters > > Note, to circle back on this: > > > On Fri, Jun 7, 2013 at 1:13 PM, Richard Barnes <rbarnes@bbn.com> wrote: > > [Moving over to public-webcrypto, since I think that's where this > > should be.] > > > > On Jun 7, 2013, at 2:53 PM, Ryan Sleevi <sleevi@google.com> wrote: > > > >> On Fri, Jun 7, 2013 at 8:01 AM, Richard Barnes <rbarnes@bbn.com> > wrote: > >>> Dear WebCrypto group, > >>> > >>> Vijay and I took an action at the F2F to look into resolving the current > ambiguity around algorithm identifiers and parameters. > >>> <http://www.w3.org/2012/webcrypto/track/actions/83> > >>> <http://www.w3.org/2012/webcrypto/track/issues/12> > >>> Some analysis and a proposal are below. Comments welcome! > >>> > >>> Thanks, > >>> --Richard > >>> > >>> > >>> PROBLEM STATEMENT > >>> ================= > >>> > >>> Currently, the AlgorithmIdentifier structure appears in three places in the > API, with slightly different flavors and semantics. > >>> 1. As an input to key creation (generate / import / derive) 2. As an > >>> output of key creation (as key.algorithm) 3. As an input to crypto > >>> operations > >>> > >>> That means that the API implementation has two difficult jobs. First, it has > to translate type (1) identifiers to type (2) identifiers, e.g., by removing > generation parameters like "modulusLength". > >> > >> Can you clarify how you arrived at this interpretation? The spec > >> doesn't say one way or the other, so I'm curious the thinking. > > > > When you sit down to write generateKey/importKey, you need to figure out > how to populate the fields in the Key interface based on the arguments listed > in the spec. That raises the question of what value to assign to > "key.algorithm". The only plausible choice is from the "algorithm" parameter, > but that's got things like modulusLength that you don't necessarily want to > keep around, because they don't really match the semantic of "key.algorithm" > (namely "What can this key do?"). > > There's no other way to inquire about the keys modulusLength EXCEPT > through key.algorithm. And it's entirely reasonable to want to know that at a > later point - eg: is this a 1024-bit key or a 2048-bit key. > Especially when you factor in named key discovery or key agility, you may > wish to say "Well, I have a 1024-bit key, but I want to replace it with a 2048-bit > key" or "This key is too small for this security guarantee - let's create a new > one" > > So yes, I think (as you noted originally) we'd have to key generation > parameters attached to the Key, in addition to what you term as the algorithm > parameters. So I don't really buy the 'savings' argument here. > > > > > > (I tried to find the right citation in the spec, but > > generateKey/importKey are underspecified right now.) > > > > > >>> Second, it has to compare type (2) and type (3) identifiers, e.g., ignoring > operation parameters like "iv". > >> > >> Can you clarify when or why? > > > > The only point to having key scoping with key.algorithm is if it's enforced by > the API. So when an algorithm is invoked, the implementation of, say, > encrypt() needs to compare key.algorithm with the algorithm parameter > provided in the method call. > > I'm not 100% in agreement with you here. > > First, on the implementation side, yes, there will always be an implicit > comparison, because library keys of Alg X can only be used with Alg X in > practice. > > Second, I agree, that the algorithm-specific operation descriptions > (eg: the flow for decrypt) are not fully clear on making a *standard* > comparison, so I agree, it needs to be added. > > That said, both objects are dictionaries - There's no requirement for a strict > equality check here, this is just good ol 'duck typing'. Even when we fix > key.algorithm to be an Interface, rather than a Dictionary, it still matches the > Dictionary requirements. > > Practically speaking, I anticipate the only comparisons being > Norm(operation.algorithm).name == Norm(key.algorithm).name. > Just to be clear - An HMAC key would be independent of the included hash algorithm. And tainting would never be included in comparisons then. I don't know that this is wrong, but I am not sure it is right either. Jim > > > > (Same deal with the spec here. Would be good to require this > > comparison explicitly.) > > > > > > > >>> > >>> There aren't any algorithms defined for these transformations and > >>> comparisons, so the result is developer confusion. (I've actually > >>> gotten more than one phone call.) > >>> > >>> > >>> ANALYSIS > >>> ======== > >>> > >>> It seems like there are three types of parameters running around here: > >>> > >>> * Algorithm Parameters - Things you want to be fixed for the life > >>> of a key > >>> * Operation Parameters - Things that may vary over the lifetime of > >>> a key > >>> * Generation parameters - Things that cannot be changed without > >>> making a new key > >>> > >>> A first pass at a taxonomy of parameters is in a Google Spreadsheet here: > >>> > <https://docs.google.com/a/ipv.sx/spreadsheet/ccc?key=0AvGS_cx3xHzXd > >>> HhhZExqOEg2NktJai1Ccnc2RUhicEE&usp=sharing> > >>> > >>> The algorithm parameters really identify the algorithm itself -- the thing > that you want to check when you're doing an operation. Conversely, the > operation parameters are the things you want to ignore in that check. > Likewise, the generation parameters are the things that you don't need to > keep in the algorithm identifier after key generation. > > I'm not sure I grok the distinction you're making with algorithm and generation > - other than you expect to throw generation params to the ground (unless you > keep them around) > > I'm also not sure how "fixed for the life of the key" isn't the same as "cannot > be changed without making a new key" - seems like they're the same > definition? > > >>> > >>> It's worth noting that these concepts are important mainly for encryption, > signing, and MAC. They don't matter much for digest or KDF algorithms, so we > might handle those differently. > >>> > >> > >> Richard, > >> > >> Thanks for attempting to take the time to do this. However, it seems > >> that you didn't take into consideration the concerns I raised during > >> the face to face and the previous calls that explained the design > >> rationale behind this. > > > > Looking back at the F2F minutes, it looks to me like your two major concerns > are (1) having a clear taxonomy, and (2) compatibility with legacy APIs (e.g., > PKCS11). I've tried to address the former above and in the linked > spreadsheet, and the latter below. Let me know if there are other concerns. > > > > > >> The current choice of specification was very intentional, based > >> around the limitations and designs of a number of APIs. Quite simply, > >> a number of underlying cryptographic libraries do not support what > >> you propose - most notably, PKCS#11 - and instead require much more > >> specification up front. > >> > >> Now we can debate whether we're designing for the past or for the > >> future, but I would suspect that if the Web Crypto API requires new > >> platform cryptographic APIs to be developed, then the entire value of > >> this API is lost (namely, being able to use existing, validated, > >> approved-by-various-government APIs and implementations - and to keep > >> browsers OUT of the crypto game). > >> > >> I'm all for reducing friction, but I want to make sure we recognize > >> the limitations in the design space. > > > > Funny enough, what prompted me to get around to writing this up was a > > question from a developer who's working on implementing this interface > > on top of NSS :) > > > > I'd like to make two observations here: First, that the two questions (of how > parameters are allocated here vs legacy) are pretty well decoupled. And > second, even if you don't buy the first argument, it doesn't matter, because > we're not actually changing what information the API has at any given > moment. > > > > On the first point: The points of contact between the WebCrypto API > implementation and the underlying crypto implementation are pretty well > defined. In the course of a generateKey / encrypt process (for example), the > WebCrypto software has to make two calls to the underlying library, one to > generate a key and one to encrypt data. At both points, it has all the > parameters it needs -- it has both the algorithm parameters and the > generation/operation parameters, and can use them both to fill in what the > library needs. > > > > For example, suppose you're trying to invoke GCM via PKCS11. Under this > proposal, you would get two separate inputs: > > Algorithm parameters: alg_p = { name: "AES-GCM", tagLength: 128 } > > Operation parameters: op_p = { iv: /* ... */, additionalData: /* ... > > */ } You can then fill in the CK_GCM_PARAMS as follows (assuming some JS- > to-C conversion layer): > > typedef struct CK_GCM_PARAMS { > > CK_BYTE_PTR pIv; // op_p.iv > > CK_ULONG ulIvLen; // op_p.iv.byteLength > > CK_BYTE_PTR pAAD; // op_p.additionalData > > CK_ULONG ulAADLen; // op_p.additionalData.byteLength > > CK_ULONG ulTagBits; // alg_p.tagLength > > } CK_GCM_PARAMS; > > > > On the second point: If you don't buy the argument above on the first point, > then the current API is broken. This proposal doesn't change the set of > parameters that's presented when a WebCrypto method is invoked, it just > changes where the API implementation looks for them. Namely, for > CryptoOperation calls, instead of pulling everything from the algorithm > argument, it pulls primarily from the key.algorithm, then adds the operational > parameters. So there's nothing new here. > > > > In case an example helps, one is below. > > > > Hope that this helps clarify, > > --Richard > > My concern, and from talking with others, is this adds a new layer of 'magic' to > the API, and, like defaults (which this is quite similar to), can hide things or > make it more complex. > > On the implementation practicality side, it means that the Key must always be > inspected before any algorithm normalization of the operation parameters > can be performed. > > On the end-user side, I'd argue that it makes code less reasonable. I have no > knowledge what 'op_p' will end up doing, without also understanding what > the key is. If I use the wrong key, the operation itself may still complete as > normal - because the op params are interchangable. > > > > > > > -----BEGIN-example.js----- > > //================================== > > // OLD: > > > > // Define parameters > > var alg_gen = { > > name: "AES-GCM", > > length: 128 > > }; > > var alg_enc = { > > name: "AES-GCM", > > tagLength: 128, > > iv: new Uint8Array(12), > > additionalData: new Uint8Array() > > }; > > window.crypto.getRandomValues(alg_enc.iv); > > > > // Make a key and encrypt some data > > window.crypto.generateKey(alg_gen, false, ["encrypt"]) > > .then(function(k) { > > return window.crypto.encrypt(/* TODO */); > > }) > > .then(function(encryptor) { > > /* Encrypt some data */ > > }); > > > > //================================== > > // NEW: > > > > // Define parameters > > var alg = { name: "AES-GCM", tagLength: 128 }; var gen_p = { length: > > 128 }; var op_p = { > > iv: new Uint8Array(12), > > additionalData: new Uint8Array() > > }; > > window.crypto.getRandomValues(op_p.iv); > > > > // Make a key and encrypt some data > > window.crypto.generateKey(alg, gen_p, false, ["encrypt"]) > > .then(function(k) { > > return window.crypto.encrypt(k, op_p); > > }) > > .then(function(encryptor) { > > /* Encrypt some data */ > > }); > > -----END-example.js----- > > > > > > > > > > > >> > >> > >> > >>> > >>> PROPOSAL > >>> ======== > >>> > >>> At a high level: > >>> 1. Split out operation and generation parameteres from algorithm > >>> parameters 2. Instead of specifying an algorithm in the > >>> CryptoOperation methods, specify key + operation parameters 3. In > >>> addition to specifying an algorithm at key generation time, specify > >>> algorithm + generation parameters > >>> > >>> This resolves the two ambiguities noted above. At key generation time, > the algorithm is copied straight through to the key, while the generation > parameters are discarded. At operation time, the algorithm is drawn from the > key, and the operation parameters specified in the method call. > >>> > >>> As a bonus, this also adds some clarify the other methods for creating Key > objects, deriveKey and importKey. In those cases, you're not generating the > key, so you just have to specify the algorithm. > >>> > >>> We might also consider exposing the generation parameters on keys. > That would allow applications to query a Key object for its properties after it's > been generated ("How long is this RSA key?"). Applications could keep track > of this information on their own, but incorporating it in the Key object would > be simple for the API code, and would save apps the bookkeeping. If we do > expose this information, it seems like it should be as a separate property from > "algorithm". > >> > >
Received on Friday, 19 July 2013 18:41:46 UTC