RE: ISSUE-12 / ACTION-83: Operation vs. Algorithm parameters

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