Subject: [Acme] Simplifying key rollover

From: Jacob Hoffman-Andrews <[email-masked]>
Date: Thu, 31 Mar 2016 14:27:48 -0700

I'd like to propose some changes to how we do account key roll-over. I
think the current approach is too complex. The current version is
included below for reference.

Instead I'd like to propose:

To update the key on a registration object, POST a double-signed JOSE
object to the registration URL, with the field "key" containing the new
key, in JWK format.

The double-signed object should have two signatures: one from the old
key, and one from the new key. This ensures that the key rollover is
authorized by the existing account holder, and also proves that the
requester possesses the private key corresponding to the new key.

What do you all think?

Relatedly, since we are introduce a self-signature to prove possession
of the new key, we probably want to introduce the same guarantee on
initial account creation. One easy way to achieve this would be to
require that the "jwk" field be provided in the protected header, rather
than in the protected or unprotected header as is allowed today.

Thanks,
Jacob

Current spec:

### Account Key Roll-over

A client may wish to change the public key that is associated with a
registration, e.g., in order to mitigate the risk of key compromise.  To do
this, the client first constructs a JSON object representing a request to
update the registration:

resource (required, string):
: The string "reg", indicating an update to the registration.

oldKey (required, string):
: The JWK thumbprint of the old key {{RFC7638}}, base64url-encoded

~~~~~~~~~~
{
  "resource": "reg",
  "oldKey": "D7J9RL1f-RWUl68JP-gW1KSl2TkIrJB7hK6rLFFeYMU"
}
~~~~~~~~~~

The client signs this object with the new key pair and encodes the
object and
signature as a JWS.  The client then sends this JWS to the server in the
"newKey" field of a request to update the registration.

~~~~~~~~~~
POST /acme/reg/asdf HTTP/1.1
Host: example.com

{
  "resource": "reg",
  "newKey": /* JSON object signed as JWS with new key */
}
/* Signed as JWS with original key */
~~~~~~~~~~

On receiving a request to the registration URL with the "newKey"
attribute set,
the server MUST perform the following steps:

1. Check that the contents of the "newKey" attribute are a valid JWS
2. Check that the "newKey" JWS verifies using the key in the "jwk" header
   parameter of the JWS
3. Check that the payload of the JWS is a valid JSON object
4. Check that the "resource" field of the object has the value "reg"
5. Check that the "oldKey" field of the object contains the JWK
thumbprint of
   the account key for this registration

If all of these checks pass, then the server updates the registration by
replacing the old account key with the public key carried in the "jwk"
header
parameter of the "newKey" JWS object.

If the update was successful, then the server sends a response with
status code
200 (OK) and the updated registration object as its body.  If the update
was not
successful, then the server responds with an error status code and a problem
document describing the error.

---

From: Richard Barnes <[email-masked]>
Date: Thu, 31 Mar 2016 19:41:31 -0500

> requester possesses the private key corresponding to the new key.

Note that we had already had some discussion of refactoring roll-over in
the thread that Karthik started.  The proposal there was:

NEW: Sign(K_new_priv, Sign(K_old_priv, Fingerprint(K_new_pub)))

That arrangement has the benefit that it makes it straightforward to do
recovery if you have a recovery MAC key provisioned:

NEW: Sign(K_new_priv, MAC(K_recovery, Fingerprint(K_new_pub)))

In both the current proposal and the new one, there are two signatures in
the recovery object, but they're nested rather than parallel.

> than in the protected or unprotected header as is allowed today.

I could live with this, but I would want more analysis to justify it.

--Richard

> _______________________________________________
> Acme mailing list
> [email-masked]
> https://www.ietf.org/mailman/listinfo/acme
>

---

From: Ron <[email-masked]>
Date: Fri, 1 Apr 2016 12:27:10 +1030

> I could live with this, but I would want more analysis to justify it.

I have something like the opposite question to that tagged in my code
at present.  Is there any reason we need the unprotected header in this
protocol at all?

I'm currently not even including it at all, and boulder accepts that
just fine, and the only difference between them is the protected copy
also contains the nonce, otherwise it's just duplicated data.

I understand why the unprotected header exists for JWS in general,
but I don't see where in _this_ protocol there is an advantage or
need to communicate something that isn't integrity protected.

Is there something I'm missing which makes that desirable here?

Otherwise, the simplification and deduplication alone makes this seem
appealing to me, and it doesn't add work that people don't already
need to do anyway.

  Ron