Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Quick fix for Ayer's duplicate-signature key selection attack #217

Closed
wants to merge 3 commits into from
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 12 additions & 15 deletions draft-barnes-acme.md
Original file line number Diff line number Diff line change
Expand Up @@ -1649,9 +1649,8 @@ The client serializes the validation object to UTF-8, then uses its account
private key to sign a JWS with the serialized JSON object as its payload. This
JWS is NOT REQUIRED to have the "nonce" header parameter.

The client will compute Z, the SHA-256 of the "signature" value from the JWS.
The hash is calculated over the base64-encoded signature string. Z is encoded
in hexadecimal form.
The client will compute Z, the SHA-256 of the JWS encoded in UTF-8.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comment/question as for DNS: why not use simple serialization instead of JWS

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I attempted to answer this after the DNS comment.

Z is encoded in hexadecimal form.

The client will generate a self-signed certificate with the
subjectAlternativeName extension containing the dNSName
Expand Down Expand Up @@ -1682,8 +1681,7 @@ validation (required, string):
Given a Challenge/Response pair, the ACME server verifies the client's control
of the domain by verifying that the TLS server was configured appropriately.

1. Verify the validation JWS using the account key for which the challenge
was issued.
1. Verify the validation JWS using the account key.
2. Decode the payload of the JWS as UTF-8 encoded JSON.
3. Verify that there are exactly two fields in the decoded object, and that:
* The "type" field is set to "dvsni"
Expand Down Expand Up @@ -1861,12 +1859,12 @@ The client serializes the validation object to UTF-8, then uses its account
private key to sign a JWS with the serialized JSON object as its payload. This
JWS is NOT REQUIRED to have the "nonce" header parameter.

The record provisioned to the DNS is the "signature" value from the JWS, i.e.,
the base64-encoded signature value. The client constructs the validation domain
name by appending the label "_acme-challenge" to the domain name being
validated, then provisions a TXT record with the signature value under that
name. For example, if the domain name being validated is "example.com", then the
client would provision the following DNS record:
The record provisioned to the DNS is the base64-encoded SHA-256 of the JWS

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just use the SHA-256 of the serialized challenge? I don't understand why the client needs to construct the JWS over the challenge if the signature never gets verified. i.e., step 5 is comparing two JWS values for equality but doesn't verify either JWS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The signature does get verified. Step 1 verifies the JWS. Step 5 verifies that the JWS (from step 1) is located in the DNS record.

It makes sure that the ACME client has control of the account key, and the authorization (DNS entry/certificate) is explicitly linked to the validation response.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I understand this issue is closed - however, I'm replying here for thread continuity.)

The signature gets verified in step 1 on the request from the client, not on the DNS value. Why not just put sha256 of the payload in DNS (instead of SHA256 of signature over the payload)?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The challenge payload doesn't contain the public portion of the account key which I believe is necessary. I was also trying to maintain some uniformity with the existing SimpleHTTP challenge (provide the full validation JWS).

Here is one attack that I was trying to protect against.

Imagine a client communicating with an "evil" ACME server (or any untrustworthy TLS endpoint). The "evil" ACME server could attain authorization over a domain it doesn't own with its own public key from a legitimate ACME server.

The evil ACME server would forward requests to the true ACME server except swap out its own public key for all of the requests. When the authorization step came, the client would unwittingly allow the evil ACME server to use the same validation challenge.

encoded in UTF-8. The client constructs the validation domain name by
appending the label "_acme-challenge" to the domain name being validated, then

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: should this be "prepending" not "appending"?

provisions a TXT record with the signature value under that name. For example,
if the domain name being validated is "example.com", then the client would
provision the following DNS record:

~~~~~~~~~~
_acme-challenge.example.com. 300 IN TXT "gfj9Xq...Rg85nM"
Expand Down Expand Up @@ -1894,15 +1892,14 @@ validation (required, JWS):

To validate a DNS challenge, the server performs the following steps:

1. Verify the validation JWS using the account key for which this challenge was
issued
1. Verify the validation JWS using the account key.
2. Decode the payload of the JWS as UTF-8 encoded JSON
3. Verify that there are exactly two fields in the decoded object, and that:
* The "type" field is set to "dns"
* The "token" field matches the "token" value in the challenge
4. Query for TXT records under the validation domain name
5. Verify that the contents of one of the TXT records match the "signature"
value in the "validation" JWS
5. Verify that the contents of one of the TXT records match the base64-encoded
SHA-256 of the "validation" JWS.

If all of the above verifications succeed, then the validation is successful.
If no DNS record is found, or DNS record and response payload do not pass these
Expand Down