go icon indicating copy to clipboard operation
go copied to clipboard

proposal: x/crypto/acme: Client.AcceptWithPayload to support device attestation extension

Open zhsh opened this issue 1 year ago • 3 comments

Proposal Details

The current RFC 8555, section 7.5.1 "Responding to Challenges" (https://datatracker.ietf.org/doc/html/rfc8555#section-7.5.1) states that the client should send an empty JSON body ("{}") to the challenge URL. That is what Client.Accept() method does: https://github.com/golang/crypto/blob/3375612bf41a8cdb0cdfdc21e6ca2c7ae0f764b5/acme/acme.go#L517

A new extension to the ACME protocol is proposed: https://datatracker.ietf.org/doc/draft-acme-device-attest/ Based on the recent IETF meetings, the proposal is likely to be accepted.

To support the new extension, the ACME client should be able to send a non-empty JSON body:

A client responds with the response object containing the WebAuthn attestation object in the "attObj" field to acknowledge that the challenge can be validated by the server.

This proposal is for adding a new method to acme.Client (perhaps client.AcceptWithPayload) that is similar to client.Accept but allows to pass a non-default payload.

zhsh avatar Jul 31 '24 06:07 zhsh

A quick note: Smallstep's implementation of ACME client and server already supports the new extension.

Smallstep client code that supports both the default and non-default payloads: https://github.com/smallstep/certificates/blob/1b2d999e4607bbe4796dce2a0f0f3c7a29cec463/ca/acmeClient.go#L243-L267

zhsh avatar Jul 31 '24 06:07 zhsh

CC @golang/security

ianlancetaylor avatar Jul 31 '24 19:07 ianlancetaylor

Change https://go.dev/cl/608975 mentions this issue: acme: support challenges that require the ACME client to send a non-empty JSON body in a response to the challenge.

gopherbot avatar Aug 28 '24 05:08 gopherbot

This proposal has been added to the active column of the proposals project and will now be reviewed at the weekly proposal review meetings. — rsc for the proposal review group

rsc avatar Aug 29 '24 00:08 rsc

Would it make sense to add a field to the Challenge type instead of having a whole new method?

rsc avatar Sep 04 '24 17:09 rsc

We could also avoid adding a field entirely, and add an internal check for Challenge.Type == "hardware-module". This would be somewhat opaque, but doesn't require an API change.

rolandshoemaker avatar Sep 04 '24 18:09 rolandshoemaker

Would it make sense to add a field to the Challenge type instead of having a whole new method?

This works too.

If I understand correctly, the change will look like the below. Does it make sense?

  1. Add a field to Challenge.
type Challenge struct {
  ...
  // If a challenge requires a non-empty response (e.g. "device-attest-01"),
  // the client must populate Response
  Response sometype
}
  1. Code that uses the new challenge type will look like this:
  // retrieve a challenge from the server
  var challenge acme.Challenge
  challenge = ...

  // construct the response from challenge.Token
  challenge.Response = ...

  // inform the server that the challenge is ready
  acmeClient.Accept(ctx, challenge)
  1. Client.Accept() is modified to handle non-empty response:
  if chal.Response is not initialized {
    res, err := c.post(ctx, nil, chal.URI, json.RawMessage("{}"), ...)
  } else {
    res, err := c.post(ctx, nil, chal.URI, chal.Response, ...)
  }

zhsh avatar Sep 05 '24 06:09 zhsh

We could also avoid adding a field entirely, and add an internal check for Challenge.Type == "hardware-module". This would be somewhat opaque, but doesn't require an API change.

I may be missing something here... How does the client pass the response to the server in this case?

zhsh avatar Sep 05 '24 06:09 zhsh

Thank you for the review and suggestions!

zhsh avatar Sep 05 '24 06:09 zhsh

BTW this ACME extension as specified in the draft is already widely implemented and deployed in iOS and MacOS, so even if the draft evolves further (which is unlikely; it's AFAIK at the IETF last-call stage), there will still be a desire to support it for this large client base.

djm-google avatar Sep 10 '24 00:09 djm-google

I may be missing something here... How does the client pass the response to the server in this case?

Whoops, my bad I was thinking of the wrong extension. Adding a field to Challenge makes the most sense. The description in https://github.com/golang/go/issues/68674#issuecomment-2330712520 sounds accurate to me.

rolandshoemaker avatar Sep 10 '24 20:09 rolandshoemaker

OK, so it sounds like maybe adding

// Payload is the JSON-formatted payload that the client sends
// to the server to indicate it is ready to respond to the challenge.
// When unset, it defaults to an empty JSON object: {}.
Payload json.Message

to Challenge is all we need?

rsc avatar Sep 11 '24 17:09 rsc

OK, so it sounds like maybe adding

Payload json.Message

to Challenge is all we need?

This looks like the best path forward. Just a minor clarification: should the type be json.RawMessage?

zhsh avatar Sep 17 '24 06:09 zhsh

The current proposal is to add the following field to acme.Challenge:

// Payload is the JSON-formatted payload that the client sends
// to the server to indicate it is ready to respond to the challenge.
// When unset, it defaults to an empty JSON object: {}.
Payload json.RawMessage

Is that right?

aclements avatar Sep 25 '24 18:09 aclements

I would like for the doc comment to be a little more prescriptive. Perhaps point at the extension spec and/or suggest why one would want to set this field. Could someone more familiar with the uses of this write that text?

Also, I see the extension proposal hasn't made any visible progress since version 1 was uploaded on 2023-07-24. Do we expect this to actually get ratified? Alternatively, is it in widespread enough use that it's de facto standard?

aclements avatar Sep 25 '24 18:09 aclements

I would like for the doc comment to be a little more prescriptive. Perhaps point at the extension spec and/or suggest why one would want to set this field. Could someone more familiar with the uses of this write that text?

Yup I can add some additional text.

Also, I see the extension proposal hasn't made any visible progress since version 1 was uploaded on 2023-07-24. Do we expect this to actually get ratified? Alternatively, is it in widespread enough use that it's de facto standard?

It looks like the most recent revision was sent last month (draft-acme-device-attest-03), given there has been minimal feedback, it's likely to get moved to last-call at the next IETF meeting in November. The acme WG has taken a somewhat more reasonable approach to specification standardization, usually waiting for at least a couple working implementations before fully ossifying things.

rolandshoemaker avatar Sep 25 '24 18:09 rolandshoemaker

Have all remaining concerns about this proposal been addressed?

Add the following field to acme.Challenge:

// Payload is the JSON-formatted payload that the client sends
// to the server to indicate it is ready to respond to the challenge.
// When unset, it defaults to an empty JSON object: {}.
Payload json.RawMessage

aclements avatar Oct 04 '24 00:10 aclements

Alternatively, is it in widespread enough use that it's de facto standard?

Yes, the current draft is already implemented by IOS and MacOS.

djm-google avatar Oct 04 '24 17:10 djm-google

Based on the discussion above, this proposal seems like a likely accept.

Add the following field to acme.Challenge:

// Payload is the JSON-formatted payload that the client sends
// to the server to indicate it is ready to respond to the challenge.
// When unset, it defaults to an empty JSON object: {}.
Payload json.RawMessage

aclements avatar Oct 23 '24 19:10 aclements

No change in consensus, so accepted. 🎉 This issue now tracks the work of implementing the proposal.

Add the following field to acme.Challenge:

// Payload is the JSON-formatted payload that the client sends
// to the server to indicate it is ready to respond to the challenge.
// When unset, it defaults to an empty JSON object: {}.
Payload json.RawMessage

aclements avatar Oct 30 '24 19:10 aclements