Re: [Acme] Signing HTTP Messages

Richard Barnes <rlb@ipv.sx> Sat, 20 December 2014 17:32 UTC

Return-Path: <rlb@ipv.sx>
X-Original-To: acme@ietfa.amsl.com
Delivered-To: acme@ietfa.amsl.com
Received: from localhost (ietfa.amsl.com [127.0.0.1]) by ietfa.amsl.com (Postfix) with ESMTP id 5CF211AC3DF for <acme@ietfa.amsl.com>; Sat, 20 Dec 2014 09:32:14 -0800 (PST)
X-Virus-Scanned: amavisd-new at amsl.com
X-Spam-Flag: NO
X-Spam-Score: -1.977
X-Spam-Level:
X-Spam-Status: No, score=-1.977 tagged_above=-999 required=5 tests=[BAYES_00=-1.9, FM_FORGED_GMAIL=0.622, HTML_MESSAGE=0.001, RCVD_IN_DNSWL_LOW=-0.7] autolearn=ham
Received: from mail.ietf.org ([4.31.198.44]) by localhost (ietfa.amsl.com [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id cfpQBUmZzdtt for <acme@ietfa.amsl.com>; Sat, 20 Dec 2014 09:32:10 -0800 (PST)
Received: from mail-lb0-f180.google.com (mail-lb0-f180.google.com [209.85.217.180]) (using TLSv1 with cipher ECDHE-RSA-RC4-SHA (128/128 bits)) (No client certificate requested) by ietfa.amsl.com (Postfix) with ESMTPS id 130F21A8A55 for <acme@ietf.org>; Sat, 20 Dec 2014 09:32:10 -0800 (PST)
Received: by mail-lb0-f180.google.com with SMTP id l4so2206204lbv.25 for <acme@ietf.org>; Sat, 20 Dec 2014 09:32:08 -0800 (PST)
X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20130820; h=x-gm-message-state:mime-version:in-reply-to:references:date :message-id:subject:from:to:cc:content-type; bh=0z1AZu8N96nK8FaTH3QpTLAsuVfBFSMPhWpjBZCNmcE=; b=bfRAfunrssZ5v+56ZIMlHW5vt44N5QfSDvWC4D3y/rQvmYaBfmRQbN+vv9UMM3WYPw b+/acGEA3rqoc7HdhV7Slc33CC0BXLhNu1CKassUvUlT0nEjs7KCbq8EVNUFy+G+DEUn Yfr4IMSu9zu39xXYpES00bxh1WjzabdEmOmE1RlKd05QUdKz+vWd61FzyrVZCQa9/TrJ eV8/VjvAiCfC8FkehUZk6MEIZBYWS34dm7Jmsi5ZR7I38lPdCT7NN+xeaKhxgZosXk6b WbXeklfQ3GJ0Nvcu7wdImbOfxRaaYQ4B471FBbkJW26Bqn+ok034y+fyFOSqMGpCqe0S WGEg==
X-Gm-Message-State: ALoCoQkBpJXtGQY8Nl/RuDWwnHdZAqQoEm73xq9TVztiT5feS06Cbr4tfOnYu1COqfKKpvlc1NX/
MIME-Version: 1.0
X-Received: by 10.152.26.201 with SMTP id n9mr13784327lag.50.1419096728506; Sat, 20 Dec 2014 09:32:08 -0800 (PST)
Received: by 10.25.12.215 with HTTP; Sat, 20 Dec 2014 09:32:08 -0800 (PST)
In-Reply-To: <5494E2A6.3020508@digitalbazaar.com>
References: <5494AE04.6070207@digitalbazaar.com> <CAL02cgRH8gNg2TKr+uEnFtmnQm0eR_=pQhFpUVPePqT5c9t6Pg@mail.gmail.com> <5494E2A6.3020508@digitalbazaar.com>
Date: Sat, 20 Dec 2014 12:32:08 -0500
Message-ID: <CAL02cgQ=VouAhvZfeW9Sw7U+KJN6pZ-2oA0ck9YD4kfW-oh9Zg@mail.gmail.com>
From: Richard Barnes <rlb@ipv.sx>
To: Manu Sporny <msporny@digitalbazaar.com>
Content-Type: multipart/alternative; boundary="089e0160a70667a970050aa9343b"
Archived-At: http://mailarchive.ietf.org/arch/msg/acme/4yxjvBxdp1EwQ8eOpO_T_-kVG7E
Cc: Phillip Hallam-Baker <phill@hallambaker.com>, ACME <acme@ietf.org>
Subject: Re: [Acme] Signing HTTP Messages
X-BeenThere: acme@ietf.org
X-Mailman-Version: 2.1.15
Precedence: list
List-Id: Automated Certificate Management Environment <acme.ietf.org>
List-Unsubscribe: <https://www.ietf.org/mailman/options/acme>, <mailto:acme-request@ietf.org?subject=unsubscribe>
List-Archive: <http://www.ietf.org/mail-archive/web/acme/>
List-Post: <mailto:acme@ietf.org>
List-Help: <mailto:acme-request@ietf.org?subject=help>
List-Subscribe: <https://www.ietf.org/mailman/listinfo/acme>, <mailto:acme-request@ietf.org?subject=subscribe>
X-List-Received-Date: Sat, 20 Dec 2014 17:32:14 -0000

On Fri, Dec 19, 2014 at 9:44 PM, Manu Sporny <msporny@digitalbazaar.com>
wrote:
>
> On 12/19/2014 07:35 PM, Richard Barnes wrote:
> > Thanks for reaching out.  Just so the context is clear, there's
> > nothing in ACME currently that uses an HTTP header to convey a
> > signature structure.  It's all in the body.
>
> Good to know. My assumption was that ACME is being built on top of JOSE.
>
> > The draft-cavage- document had been pointed out to me before.  It's
> > too ambitious :)
>
> Haha, we were trying to do something sensible, not ambitious. :)
>
> > That draft tries to solve the problem of signing an HTTP message.
> > That's a fiendishly difficult problem because it involves headers,
> > whose complicated, ambiguous syntax is inimical to signing. Also,
> > middleboxes routinely tamper with headers.
>
> Yes, both rabbit-holes were taken into account when designing the spec.
> Just to be clear, we're not trying to sign /all/ HTTP headers in a
> message, the sender gets to pick which headers to sign, the server gets
> to decide whether or not that's good enough for the type of request.
>
> The developer can choose specific headers that don't have ambiguous
> syntax and ones that middleboxes are most likely not to modify.
>
> I think we address these two concerns in the "Signing HTTP Messages"
> spec as best we can.
>
> > All I want is to sign the body of the message.  HTTP treats the body
> > as an octet string, which means there's no c14n issues.  And
> > middleboxes tamper with bodies much less often.
>
> I think you can do this easily with the Signing HTTP Messages spec. Just
> digitally sign the Digest header (along with a few others to prevent
> replay attacks if doing so is important to the use case).
>
> There's an example of doing this in this section:
>
> https://tools.ietf.org/html/draft-cavage-http-signatures-03#section-3.1.2
>
> > Also, the draft-cavage- document invents its own signature syntax,
> > when it should just use JWS.
>
> What do you mean by "signature syntax"? Do you mean "the way the bytes
> are constructed in order to sign them"?
>
> The normative bits of the Signing HTTP Messages spec are roughly 5 pages
> in length vs. the 50+ pages in JWS. It didn't seem necessary to pull in
> all that unnecessary bloat just for signature string construction.
>
> It's true that maybe the length of the JWS spec is due to necessary
> security precautions that the Signing HTTP Messages spec overlooks, but
> if it does overlook something, we're not aware of it.
>
> Happy to be convinced otherwise, but I'd rather not expose implementers
> to JWS or JOSE in general.
>
> > So my proposal was: Start with a Content-Signature header that just
> > has a JWS covering the body.
>
> Sounds like a good idea. I think you could simplify it further and make
> it easier to implement.
>
> > In some cases that's all you want, and it's a more tractable problem
> > than covering HTTP messages as a whole.
>
> I think you may not quite understand what we're trying to do w/ that
> spec. Or, I don't understand the nuance that you're getting at. Either
> way, we're miscommunicating. :)
>
> > Then, if you want to protect HTTP headers later, you can add a
> > signed attribute to the JWS (e.g.,
> > digest(canonicalized-header-info)).
> >
> > Does that make sense?   Is that at all relevant to your use cases?
>
> What you're trying to do certainly makes sense, and if you want to use
> JWS to solve the problem, I guess you could do that. I still hold that
> the Signing HTTP Messages spec sounds like it could solve your problem
> in a much simpler way. For example, here's what a full solution might
> look like:
>
> ---------------------
> POST /foo HTTP/1.1
> Host: example.org
> Date: Tue, 07 Jun 2014 20:51:35 GMT
> Content-Type: application/json
> Digest: SHA-256=X48E9qOokqqrvdts8n...yWxBf7kbu9DBPE=
> Signature: keyId="Test",algorithm="rsa-sha256",
>   headers="(request-target) host date digest content-length",
>   signature="jgSqYK0yKclIHfF9zdA...NqNyAXKdxZZItOuhIs78w="
> Content-Length: 18
>
> {"hello": "world"}
> ---------------------
>


Just for context, here's the equivalent using JWS and protecting only the
body:

---------------------
POST /foo HTTP/1.1
Host: example.org
Date: Tue, 07 Jun 2014 20:51:35 GMT
Content-Type: application/json
Digest: SHA-256=X48E9qOokqqrvdts8n...yWxBf7kbu9DBPE=
Content-Signature: { "header": { "kid": "Test", "alg": "RS256" },
"signature": "jgSq...78w" }
Content-Length: 18

{"hello": "world"}
---------------------

Note that using JWS means that I can just toss the header into
JSON.parse(), whereas your construction requires custom parsing.  And if I
have a JOSE library already on hand (of which there are currently a
non-trivial number), I can just toss the whole thing in there and call
Verify().

Also, one thing I like about JWS is that you can make it self-verifying.
That is, you can replace the key ID ("kid") with the actual public key
("jwk"), so the verifier can just go ahead and verify without any
pre-provisioning of key IDs.  You can also include metadata like certs.
 (Yes, that starts to make the header a little big.  Deployment
consideration :) )

I wouldn't overestimate the complexity of JWS.  Lines of code is a better
metric than lines of spec.  It took me a couple of hours to write a JWS
library in Go; signing and verification take ~110 lines, most of which is
just algorithm mux/demux.
https://github.com/bifurcation/gose/blob/master/jws.go#L139

Finally, if you want to protect headers, you can add a protected header to
the above to bind in the header string by reference:

---------------------
{
  "header": { "kid": "Test", "alg": "RS256" },
  "protected": base64({
    "header": {
      "fields": "(request-target) host date digest content-length",
      "sha256": "KCBeV_VF5cEzgjY1cNEAkalw94mJwxtXdz874aRUbRM"
  })
  "signature": "jgSq...78w"
}
---------------------

... where the "sha256" field is the SHA-256 digest of the header string.

Is this making any more sense?  Basically: Use libraries and scale the
complexity to the problem.

--Richard



> Thoughts?
>
> -- manu
>
> --
> Manu Sporny (skype: msporny, twitter: manusporny, G+: +Manu Sporny)
> Founder/CEO - Digital Bazaar, Inc.
> blog: The Marathonic Dawn of Web Payments
> http://manu.sporny.org/2014/dawn-of-web-payments/
>