w3c-ccg / http-signatures

Signing HTTP Messages specification
https://w3c-dvcg.github.io/http-signatures/
Other
34 stars 9 forks source link

Creating and Signing the Signature String #54

Open RayTrommelenInfoSupport opened 5 years ago

RayTrommelenInfoSupport commented 5 years ago
  1. Use the headers and algorithm values as well as the contents of the HTTP message, to create the signature string.
  2. Use the key associated with keyId to generate a digital signature on the signature string.
  3. The signature is then generated by base 64 encoding the output of the digital signature algorithm.

There are a few things with this list I'd like to make a pull request for. Before I'll make some changes, I was wondering what others thought about it.

First, to me it's not clear that the signature string should be hashed. I'd suggest adding an extra point, and stating the Signature String should be hashed.

Secondly, it states that the contents of the HTTP message should be used to create the Signature String. To me, this means that the body of the message should be added, but this not stated anywhere else in the specification. Thus, this confuses me and I wonder if this has become irrelevant?

Based on these points, I thought the next list could replace it.

  1. Use the headers value to create the signature string.
  2. Use the hashing algorithm found in algorithm to create a hash of the signature string.
  3. Use the key associated with keyId to generate a digital signature on the signature string.
  4. The signature is then generated by base 64 encoding the output of the digital signature algorithm.
ioggstream commented 5 years ago

Yes, there are some quirks. Probably using google docs to discuss those editorial points could be of some benefit. I can share a copy if you agree.

RayTrommelenInfoSupport commented 5 years ago

@ioggstream Yes, I'd be interested in that.

ioggstream commented 5 years ago

Here it is @msporny @RayTrommelenInfoSupport https://docs.google.com/document/d/1EHOcsZmKodlgodMZo4zdhEBLBjZRyeFB1BeOsQEzrDw/edit?usp=sharing

liamdennehy commented 5 years ago
  1. Use the hashing algorithm found in algorithm to create a hash of the signature string.

This is already implicit in the signature scheme. Without getting into the details, implementations will generally invoke a library to sign content, which includes the hash function since one rarely wants to sign a message directly (and cannot if the message is too long vs the key length). While the canonicalisation is novel (how to generate the string) the actual signature is not and we should refer to the existing literature. We already reference RFCs 6234, 8017 & 8032, and should encourage implementers to re-use these functions rather than juggling hashes.

dlongley commented 5 years ago

@liamdennehy,

This is already implicit in the signature scheme. Without getting into the details, implementations will generally invoke a library to sign content, which includes the hash function since one rarely wants to sign a message directly (and cannot if the message is too long vs the key length). While the canonicalisation is novel (how to generate the string) the actual signature is not and we should refer to the existing literature. We already reference RFCs 6234, 8017 & 8032, and should encourage implementers to re-use these functions rather than juggling hashes.

+1 ... I think the reason hashes started leaking into the canonicalization algorithm is because we deprecated algorithm in favor of using the key ID to determine which algorithm to use -- and -- some people have RSA keys that can be used to sign with different hash algorithms. However, I think that's a bad security practice and we should assume that you MUST be able to determine the signing algorithm (including any hashing) from the key ID and not get involved in hashing here ourselves. I don't think we need to support people who aren't using best practices, like one key per signature scheme (including the hash to use).

The only other reason I can think of to consider "pre-hashing" the canonicalized string is to provide better support for hardware secured devices with limited bandwidth/memory.

RayTrommelenInfoSupport commented 5 years ago

You have some good points and I can stand behind the recommendation from @dlongley send me trough E-mail.

My recommendation is that we should say that users should store the hashing algorithm associated with a key when it is generated. Users should adopt the policy that no single key should be used with more than one signature scheme, where the scheme includes both the signature method and the hashing method.

Also, The Signature String is not a result of hashing the string containing the headers with their values.

I'm still not sure what we should do about the text

Use the headers and algorithm values as well as the contents of the HTTP message

The HTTP Message, or 'payload' how it is in the Google Docs, is not mentioned anywhere else in the specification.

Is the Message/Body a proposal to add to the signature string in the specification? Or is it leftover from previous drafts?

ioggstream commented 5 years ago

@RayTrommelenInfoSupport I think we could create a PR with your suggestion and change the introduction too just referencing HTTP headers.

1. Use the headers value to create the signature string.
liamdennehy commented 5 years ago

The HTTP Message, or 'payload' how it is in the Google Docs, is not mentioned anywhere else in the specification.

Is the Message/Body a proposal to add to the signature string in the specification? Or is it leftover from previous drafts?

The HTTP Message is not equivalent to the payload, but rather the entire message transmitted as either a request or response. See RFC7230 2.1:

The following example illustrates a typical message exchange...

The payload is only the body, as per RFC7230 3.3:

The message body (if any) of an HTTP message is used to carry the
payload body of that request or response.  The message body is
identical to the payload body unless a transfer coding has been
applied, as described in Section 3.3.1.

In that respect, the message is used to find the content to be signed according to the specification of the headers value to determine the headers and pseudo-headers to include in the signature string (canonicalisation). If anything, the language should be:

1. Use the headers and algorithm values as well as the headers of the
   HTTP message, to create the signature string.
liamdennehy commented 5 years ago

However, I think that's a bad security practice and we should assume that you MUST be able to determine the signing algorithm (including any hashing) from the key ID and not get involved in hashing here ourselves

This is impossible. RSA and EC keys do not contain a hash or hash specification, and while a certificate does have a hashed signature it is only what the issuing CA decided to use to sign the certificate - it is not relevant to any signature produced by the associated private key. For example, a certificate signed with sha256WithRSAEncryption can still be used to produce a RSA signature with any of SHA-1, SHA-2 or SHA-3.

Even the "fingerprint" of a certificate, often used as an identifier, is not stored in the certificate itself, but derived from the the entire certificate as represented in DER format, and can be generated by any hash algorithm, as illustrated:

$ openssl x509 -in my.crt -noout -fingerprint
SHA1 Fingerprint=1E:6D:6A:6E:1A:D1:A0:86:F1:F1:4F:5B:47:BC:38:A7:BC:33:3F:BE
$ openssl x509 -in my.crt -noout -fingerprint -sha256
SHA256 Fingerprint=2B:FB:1D:03:2E:D2:A5:84:62:7C:3D:BC:FA:6B:0C:C8:4B:0E:BB:3C:FB:F6:18:3C:A3:99:EC:8C:71:62:D7:F5

Trying to deduce which algorithm to use is also not possible just by looking at the produced signature, e.g. SHA2-256 and SHA3-256 both produce 256-bit hashes.

The hash algorithm is not a parameter to the signature algorithm itself, but rather produces the (short) data to be fed into that algorithm. EC and RSA signature schemes all operate independently and in the same way regardless of the specific hash fed into them by a particular hash algorithm.

We cannot deduce the hash to be used from the key or certificate. Implementers MAY require a particular hash be used for a given key, but this is a detail and decision of the implementer, not something we can assume is readily derived in all instances.

RayTrommelenInfoSupport commented 5 years ago
1. Use the headers and algorithm values as well as the headers of the
   HTTP message, to create the signature string.

According to the Internet-Draft, the signature string is the headers and their values in the way it's defined in the Creating the Signature String paragraph.

So, wouldn't we need to remove the 'algorithm' as well from this?

liamdennehy commented 5 years ago

So, wouldn't we need to remove the 'algorithm' as well from this?

Just as soon as you have a reliable way to determine the hash algorithm from the key metadata, which I present in https://github.com/w3c-dvcg/http-signatures/issues/54#issuecomment-494752250 is not reliably possible.

dlongley commented 5 years ago

@liamdennehy,

This is impossible. RSA and EC keys do not contain a hash or hash specification, and while a certificate does have a hashed signature it is only what the issuing CA decided to use to sign the certificate - it is not relevant to any signature produced by the associated private key.

While keys are independent from the hashing method (if any, depending on the signature scheme), that does not mean that a particular instance of a key should not be bound to using a particular hash algorithm. It's also not impossible to do this in general, though I submit that you may not be able to ascertain this information when the key identifier can only be resolved to an X.509 certificate that lacks the requisite information. Our usage, for example, did not have this problem as we used Linked Data key descriptions not X.509 certificates.

We'll need to decide how to support the existing X.509 certificate use case; either we tell users that they must track this additional constraint separately when using the new version of the spec or do what we're doing with defaults. Ideally, we would not bring back the potential attack vector that allows an adversary to control which hash is used to check the signature, even it's quite difficult to produce a real problem.

ioggstream commented 5 years ago

iiuc, JWS links hash and algorithms https://tools.ietf.org/html/rfc7518#section-3.1

We could adopt that table or create our one. In both case it would be difficult to remove algorithm.

Another thread will be whether to add the algorithm (as key + hash) to the signature string.

liamdennehy commented 5 years ago

Ideally, we would not bring back the potential attack vector that allows an adversary to control which hash is used to check the signature, even it's quite difficult to produce a real problem.

We can clearly state the implications of being open on hash algorithm choice, and recommend implementations be strict about the algorithms they accept. Note that we aren't prescriptive of the Digest header which faces the same issue, even though we explicitly illustrate how to incorporate it here.

We've already deprecated SHA1, but going further and listing algorithms in this specification is problematic - ten years from now hopefully this specification will still be valid, but the algorithms may well have moved on. The JWS spec linked above makes no mention of SHA3 for example, although this is gaining a lot of traction. We can certainly reference it (or another source), bit I don't think we should be reinventing the wheel, and then only as a suggestion.

Implementations are always responsible for the eventual choices they make and should be free to do as they need. We need to describe how a signer specifies the hash, but an implementation that accepts SHA2-512 and MD5 as equally valid is beyond our control. While we don't have a solid way of describing which hashes an implementation would accept, we also can't expect every decision to be handled in this protocol.