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

On Mon, Apr 15, 2013 at 12:03 PM, Mark Watson <watsonm@netflix.com> wrote:
>
>
> On Mon, Apr 15, 2013 at 11:32 AM, Ryan Sleevi <sleevi@google.com> wrote:
>>
>> 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.
>
>
> Here's the summary of the current algorithm and operation parameters that I
> circulated earlier:
>
> https://docs.google.com/document/d/1fKtA6U0b_R-kml3qgvdnq-diu3oyDsFbEGKG2AB-tq0/edit?usp=sharing
>
> It's possible that my concerns can be addressed by some things which are
> mainly cosmetic. What I'm sure of is that the present situation where the
> same AlgorithmIdentifier structure, with different data types for the params
> member when used in different methods (generate, sign etc.) is confusing.
> I'm sure because of empirical evidence in many emails exchanges with my
> colleagues, including those who fully qualify as crypto experts ;-)
>
> I think the simplest concrete solution would be
> 1) to define a new OperationParameters dictionary, from which the various
> dictionaries used with sign, verify, encrypt, decrypt and derive inherit
> 2) replace the AlgorithmIdentifier parameter to those operations with
> OperationParameters
> 3) decide whether hash is a property of a HMAC key, or an input into an HMAC
> operation

I'm sorry if I'm missing something, but based on this, it seems the
entirety of the argument just boils down to "Call things differently
in the spec"? That is, rather than call it Tomato, we call it Tomato?

AFAICT, your proposal has absolutely no functional change, it's just
trying to ascribe a bit of syntactic sugar. Or is it just that you
don't want to have "name" included in the dictionary? Richard's
proposal was to flatten name/params, which seems universally agreed
upon, so the only functional change seems to be that omission?

Have I missed something?

>
>>
>>
>> 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.
>
>
> This could be addressed by defining a specific WebCrypto algorithm name for
> raw keying material. The only thing you can then do with those kind of Keys
> is derive other keys.

Let's punt that for a separate discussion.

My point in raising it was merely making sure that, like so many of
these issues, that we don't get narrow tunnel vision and lose sight /
forget how we came to where we are, or how it fits in the overall
picture.

>
>>
>>
>> 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.
>
>
> I'm not convinced that the danger of accidentally using an RSA-PSS key for
> some RSA-PKCS#1v1.5 operation is any worse than the danger of accidentally
> using the wrong RSA-PKCS#1v1.5 key.

Using the wrong key won't compromise the key material itself - it will
yield a valid, but unverifiable, signature. Mixing algorithms most
commonly will end up leaking either plaintext or key material - a
vastly different scenario.

>  Should we have the developer assign an
> "id" to each key when creating this and then repeat that "id" when using it,
> just to check they're using the right one ?

No.

> Should the algorithm parameters
> specified at key creation be repeated when using the key - just to check
> it's the right one ? Just checking the algorithm name and nothing else seems
> arbitrary.

As we've been discussing with RSA, it's entirely likely that we need
to move / duplicate more of the algorithm parameters inside the
generate option - it seems entirely pointless (and perhaps dangerous)
to consume entropy in generating an RSA key that won't be usable.

A concrete example is an implementation that supports the use of
RSA-2048 bit keys with RSA-PSS using mgf1 with SHA1 or SHA256, but
doesn't support RSA-4096 bit keys nor SHA-512.

This is an issue regardless of whether we're talking about a single
browser implementation or talking about using this as an abstraction
atop platform-native/smart cards (as so many members seem to focus on,
despite the attempts at the charter to narrow focus).

So I don't think we're just talking about checking algorithm name here.

>
> It's unlikely that such mistakes would result in a system that worked at
> all. The mistakes that we can most usefully guard against are those where
> the system might appear to work, but in fact has hidden weaknesses, such as
> using the same key with different algorithms.
>
> We then have to balance against the confusion caused by the redundant
> information (the algorithm name) and the
> similar-looking-but-really-very-different data structures
> (AlgorithmIdentifier when used for key generation/import vs
> AlgorithmIdentifier when used for encrypt/decrypt/sign/verify etc.

That's just a WebIDL naming issue more than a functional issue. I
don't have strong feelings on whether we call a tomato a tomato or a
potato a potato, but I'm not sure I buy the argument it actually
impacts end users, since the whole notion of the dictionaries type is
entirely opaque to both authors and implementors, because of the
ducktyping.

>
> ...Mark
>
>>
>>
>>
>> >
>> > 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 19:21:00 UTC