Re: AlgorithmIdentifier in encrypt/decrypt/sign/verify operations

On Sat, Mar 30, 2013 at 9:52 AM, Mark Watson <watsonm@netflix.com> wrote:
>
>
> On Fri, Mar 29, 2013 at 11:22 AM, Richard Barnes <rbarnes@bbn.com> wrote:
>>
>>
>> On Mar 27, 2013, at 9:10 PM, Mark Watson <watsonm@netflix.com> wrote:
>>
>> > This may be related to ISSUE-12 and apologies again if this has been
>> > discussed before - it is coming up now frequently in implementation
>> > discussions.
>> >
>> > In the encrypt/decrypt/sign/verify operations, two AlgorithmIdentifier
>> > objects are provided as input, one as an explicit parameter and one which is
>> > associated with the Key object (and appears as the Key.algorithm attribute).
>> > Presumably it is an error if the "name" member of the dictionary does not
>> > match (after normalization), though I am not sure if this is clearly
>> > specified.
>> >
>> > In some cases, it is specified that the params member will have
>> > different types in these two places (I'm assuming that the Key.algorithm
>> > attribute takes the value that was provided to generateKey). For example for
>> > AES-CTR, the params in Key.algorithm contains the key length and params in
>> > encrypt/decrypt contains the IV.
>> >
>> > But for other cases things are very unclear. For example, for HMAC, the
>> > same AlgorithmParameters type is used, containing the hash algorithm. In
>> > this case it seems completely redundant to provide the same object twice to
>> > the sign/verify call (once in the method parameters and again in the
>> > Key.algorithm attribute).
>> >
>> > Am I missing something ? Does anyone else find this confusing ?
>> >
>> > I think the confusion could be resolved by
>> > (i) replacing the AlgorithmIdentifier argument to
>> > sign/verify/encrypt/decrypt with AlgorithmParameters.
>> > (ii) for HMAC, the params provided to sign/verify must be null, as the
>> > hash algorithm should have been provided when the Key was
>> > created/imported/unwrapped
>>
>> I agree that it could be made clearer.
>>
>> When I was implementing PolyCrypt, my read of the specification was as
>> follows:
>> 1. The algorithm provided as a parameter to encrypt() specifies the
>> encryption (and parameters)
>> 2. Throw an error if the Key.algorithm.name != algorithm.name
>>
>> That is, for the algorithm in the Key, everything besides the name is
>> ignored.  This seems right to first order, but might be wrong, e.g., for
>> HMAC, where you might want to compare the hash algorithm as well.
>
>
> It seems ambiguous to me whether the hash algorithm is a property of the key
> or a parameter to the operation. Another source of confusion.
>
>>
>>
>> I'm leery of removing the algorithm parameter from encrypt(), if only
>> because it seems really confusing and non-idiomatic.
>
>
> What idiom, and why do you say it's confusing ? The algorithm is a property
> of the key, so it's confusing that I need to re-specify it as a method
> parameter. That only seems to introduce an unnecessary failure path and give
> the incorrect impression to developer that they have some choice about the
> algorithm here. They don't. It's implicit in the Key.
>
>>
>> I don't think it's terrible to have the algorithm specified in two places,
>> as long as its clear how those two specifications relate to each other
>
>
> It's not clear now. IIUC, anything in the method AlgorithmIdentifier that is
> also a property of the Key must match. Anything else is a method parameter.
>
> A particularly confusing case is when there are both algorithm and method
> parameters. For example, suppose a create an AES-CBC key with { name :
> "AES-CBC", params: { length: 128 } }. Am I supposed to write
>
> encrypt( { name: "AES-CBC", { "length" : 128, "iv" : iv } }, ... )
>
> ?

No.

This is documented in
https://dvcs.w3.org/hg/webcrypto-api/raw-file/tip/spec/Overview.html#aes-cbc
- the encrypt() operation takes AesCbcParams, which is a dictionary
only containing a key for IV. generateKey() takes an AesKeyGenParams,
which is a dictionary containing only a key for IV.

>
> If I'm allowed to miss out the length here, why is it that it makes sense to
> miss out some of the Key properties and not others (the algorithm name
> itself) ?

>From the start, there has been immense conflict over whether or not we
would support key tainting. There has been support expressed from most
of the contributors on this thread, at some point, AGAINST key
tainting.

In such a scheme, a given key object may be thought of an algorithm
"family" - whether it be "RSA" or even potentially more specific as
"RSA-PSS" or "RSA-PKCS#1v1.5"

However, such definitions are not complete definitions from the
perspective of the security proofs and correctness. For example, one
should not assume that the hash algorithm for PKCS#1v1.5 is
interchangeable without affecting the security correctness proofs.

For this reason, because members objected to fully scoping a key's
usage at the time of creation/import, it was forced to be left to the
point of instantiating the algorithm.

The only way I see your proposal working (splitting "Algorithm" and
"Operation" parameters) is if we can fully agree on a workable,
extensible definition for what separates the two. You've picked AES
keys, which are perhaps the easiest to conceptualize, but fail to show
the edge cases. It's also easier to conceptualize because we accepted
to keep the block cipher mode (CBC, CTR, CMAC, GCM, etc) as a
*separate* algorithm, rather than as a parameter - a point which took
several months to coalesce, particularly as it related to ECB/Raw.

I would instead suggest we look at RSA keys - whether PSS, OAEP, or
PKCS#1v1.5. There are a set of parameters not strictly tied to the key
*material* (eg: the choice of hash algorithm / MGF), but which do
ultimately affect whether or not the key is supported (by the
underlying implementation) and/or whether or not it can be safely
used.

Perhaps you can make a rough sketch - and I don't mean a formal
writeup of proposed text - of a definition you see as workable (for
the current algorithms), along with a simple split.

eg:

AES:
Mode - New algorithm for each mode (CBC, GCM, CFB, CMAC)
Per-Key Params (CBC): Length
Per-Operation Params (CBC): IV
Per-Key Params (GCM): Length
Per-Operation Params (GCM): IV, AAD, Tag Length

etc

Just something that puts forward a definition that can actually be
used - or as a starting point.

Note that the discussion is similar for HMAC. There was some initial
discussions, raised by Netflix, about the conceptualization of a
"Generic" key object - that is, a key that is not part of a
private/public keypair (alternatively, a 'secret' key). The initial
discussions were regarding treating these keys as simply an opaque
handle to a series of bytes - that is, no formal algorithm attached to
them until their use. This is suitable for things like key derivation
schemes like DH, which yield a shared secret that can then be used,
either as input to a KDF or (provided there is sufficient entropy), to
use as something like a MAC key

Under your proposal, it would be necessarily to fully specify the
intended use of this key material and bind it to a fully specified
algorithm at time of creation, rather than at use. This is
particularly interesting for protocols that involve cipher
negotiations. Note that most cryptographic APIs do NOT place this
requirement on 'secret'/'generic' keys, and so the implications this
would have are, quite frankly, not well understood in the three weeks
since this proposal. If we do adopt it (which I'm not convinced it's
mature enough to vote on yet), then I think we'd at least have to
concede this as a point where we're significantly diverging for no
apparently good reason, and we must be willing to alter the design if
it turns out to be unworkable in practice.

Finally, the last argument - and arguably and unquestionably the
weakest - has to do with trying to encourage some modicum of secure
practices by providing a check on the usage of keys and key material
that prevents the accidental misuse (eg: attempting to use an RSA-PSS
key for RSA-PKCS#1v1.5). This is one of the same issues that arises
with defaults - the counter-intuitive notion that the more you have
the implementation try to 'hide' what is happening, the easier you're
making it for simple programming errors to introduce cataclysmic
security failures. There is likely a balance that can be struck here,
but it's a design consideration that has to be built in from the start
- and we should at least recognize it's something we have now in the
current specification that we'd be looking at losing.


>
> This could be completely cleaned up by only specifying the "params" member
> in the methods. Specifically by defining an OperationParameters for that
> thing and derving the operation parameters objects from that. This would
> provide a clear separation between algorithm and operation parameters.
>
> ...Mark
>
>>
>>
>>
>> > As a side note, I believe that to generate a HMAC key we need to specify
>> > the key length. At least according to FIPS 198-1 the key, K, can be of any
>> > length. So, either we require in WebCrypto that it is a particular length
>> > (say, the same size as the hash function), or we need a length parameter to
>> > generateKey for HMAC.
>>
>> +1 on adding a length parameter.
>>
>> --Richard
>>
>> >
>> > ...Mark
>>
>

Received on Monday, 15 April 2013 18:33:27 UTC