Seen this morning from @rmhrisk
Here's a direct link to the draft. Also being discussed on dev-security-policy@mozilla.org
Seen this morning from @rmhrisk
Here's a direct link to the draft. Also being discussed on dev-security-policy@mozilla.org
Is it me, or is the example of calculating the base32(sha256(accountURL)[0:9]) incorrect? I'm getting "mezdkoddg42tiytb" instead of the "ujmmovf2vn55tgye" in the draft.
Also I'm not sure why they would use [0:10]
for the first 10 characters. Luckily they explain that they mean "the first ten bytes (bytes 0 through 9 inclusive)" with that, but to me [0:10]
means the first 11 bytes.
This draft does provide some interesting possibilities I guess. It makes use of the account URL to make a static label, so you can relay the challenge to different FQDNs using CNAMEs.
I guess they use Python.
>>> list(range(11))[0:10]
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
Guess so, Python defines the [i:j]
as:
The slice of s from i to j is defined as the sequence of items with index k such that
i <= k < j
.
Ah well, they explain it well enough in the draft
Although I'm not convinced by the "Account Resource URL" definition, as RFC8555 only mentions "Account URL" without the "resource" part. They amended the definition in the latest version on Github to explain it better: Describe the ACME account resource URI by aaomidi · Pull Request #7 · aaomidi/draft-ietf-acme-dns-account-challenge · GitHub However, IMO they should just call it "Account URL" and be done with it.
Still wondering how they calculated the label tho.. Haven't figured out what I'm doing wrong or what kind of input they used. Brute forcing the SHA256 hash using just the first 10 bytes to work with is going to be difficult
I like the idea of qualified domain names but I don't like it derived from the account key. The account key will have to be more carefully managed and any (even accidental) change requires DNS adjustment. I would prefer a user-provided "suffix value" to append to _acme-challenge
This would avoid having the account key take on a new relationship. And, likely save room in DNS records as I'd suspect people would append things like "LE" or "Google" or "sys1" rather than 10 random bytes.
I'd rather have something semi-fixed than users own input though. Also, I'm pretty sure accounts are not to be ephemeral, as LE themselves use the accounts e.g. for rate limit exemptions and the E1 whitelist.
Isn't the account URI already more or less an immutable value today? Changes from the CA side would totally break just about everyone's client, no? Because the clients cache the current value and need to use it to manipulate the account object. Like, I know it's possible to re-find it using the newAccount
endpoint and the current key, but I doubt most clients are written to do that today.
It's the Account URL, not the Account Key. e.g. the string
https://acme-v02.api.letsencrypt.org/acme/acct/12345678
That URL can represent multiple AccountKeys due to rollovers, although only one is active at a time. The same AccountKey could be used on multiple services as well, each creating a different URL.
And, likely save room in DNS records as I'd suspect people would append things like "LE" or "Google" or "sys1" rather than 10 random bytes.
Individuals might do that, but this is designed for large organizations / domains. This approach allows for inter-departmental collision, in which multiple independent groups are competing for DNS records.
This notion ensures (within improbable collisions) the DNS record is associated to a single ACME CA account.
What doesn't make sense to me, is why they're using base32 and not base64 -- simply because the ACME RFC utilizes base64
Ah, ok, my bad. thanks and to @rmbolger too
I don't see how a user-provided suffix is more restrictive for cases like "inter-departmental collision" though
I think its due to base64 containing slashes, which you can't have in FQDNs.
That's why base64url exists
However, I think the reason base32 is being used due to the case insensitive nature of FQDNs/DNS. Base64 uses upper and lowercase while base32 only uses a single case.
With the hashed URI based value, the departments don't have to coordinate to prevent collisions. With anything user-provided, they would. However, if they were already coordinating and sharing a single ACME account between them, they'd have to create a new account in order to not collide (assuming they both wanted a cert for the same name).
This new challenge type in general would also solve @skreien's problem here:
IDK @rmbolger it sounds like the coordination is just different. If a company had rate limits or E1 whitelists they'd need to be sure each different account needed to avoid collision also had those features. And, that's just how LE does it.
I generally cringe when designing API's to say it is targetted at a narrow use case and to fit just that. API's evolve which is what we see here in that the DNS challenge has evolved and needs to adapt to more diverse cases.
The proposal is giving specific meaning to the account for something related to a DNS challenge. There is no obvious and required one-to-one correlation which in my mind favors not associating them in the DNS record name.
Glad you could recall that Fastly example. Regardless of what DNS record name ends up being used very definitely would have solved that one.
But isn't base32 2^32 times faster (and smaller) than base64?
[illusions of speed grandeur]
My guess is that the point was to tie the DNS record to specific account and the account URI is the only immutable value associated with an account (since even the key can be rolled over).
I generally cringe when a protocol lets users put in their own value to something
Also note that this proposal is relatively easy to implement within the current ACME ecosystem. It does not require any significant change to the communications. I.e.: only the challenge value in current exchanged messages differs, which is part of RFC 8555 anyway. Including user-defined labels would mean a significant change to the protocol with adding currently non-existent fields to current message exchanges to relay that user-defined label to the ACME server.
Yes, I see it as more convenient too. And, it solves their described problem of multiple colliding CA's. Which is the primary goal here. I still think it short-sighted.
Admittedly I don't know implementation details of ACME protocol. Nor do I have history with the RFC working group. But, my general experience says any change requires design, discussion, approval, education, documentation, and coordination efforts. The extra bit about adding a field seems trivial in comparison.
I get the same value as the draft in my test function.
First 10 bytes of SHA256(https://example.com/acme/acct/ExampleAccount)
is
162 88 199 84 186 171 123 217 155 4
which in binary grouped into 5-bit chunks is
10100 01001 01100 01100 01110 10101 00101 11010 10101 01101 11101 11101 10011 00110 11000 00100
or
20 09 12 12 14 21 05 26 21 13 29 29 19 06 24 04
and using the 0-based base32 dictionary abcdefghijklmnopqrstuvwxyz234567
becomes
u j m m o v f 2 v n 5 5 t g y e
I'm not an expert about RFCs either, but implementing just a new challenge is much easier to do than intricately modifying the existing protocol. A new challenge is just an addition to the existing RFC, while modifying ACME messages (i.e.: adding certain fields to existing messages) is probably viewed as modifying the entire protocol and thus RFC. This could be more difficult to discuss/approve/coordinate than just a new challenge.
@rmbolger Hmm, I think I know where it went wrong.. I just piped the output of sha256sum
to cut
and to base32
where it would literally take the ASCII chars a
and 2
et cetera as input instead of 0xa2 in hex form.. Using the direct bytes as output/input works:
>>> base64.b32encode(hashlib.sha256('https://example.com/acme/acct/ExampleAccount'.encode()).digest()[0:10])
b'UJMMOVF2VN55TGYE'
>>>
Not sure how to do that using Bash tho But at least the draft is OK
Ah, using xxd -r -p
to reverse the ASCII hex to actual bytes:
osiris@erazer ~ $ echo -n "https://example.com/acme/acct/ExampleAccount" | sha256sum | cut -b1-20 | xxd -r -p | base32
UJMMOVF2VN55TGYE
osiris@erazer ~ $
This uses [0:20]
as the output of sha256sum
is ASCII chars posing as hex and thus 2 ASCII chars are required for a single byte. And 2*10 = 20
It sounds like they have fed two birds with one scone by using base32.
This concern is particular to LetsEncrypt, and a scenario where all clients are using this CA. This concern doesn't exist when clients are using different CAs, or if all clients are using a CA without limits – such as a commercial CA. Remember, this proposal is from a for-profit company who runs their own ACME compliant CA. IMHO this effort is likely a step needed in an attempt to get some business-logic necessary for a proprietary tool accepted by the CA/B forum.
Sort of.
A new challenge is suggested and refined via an Internet Draft (ID) that might receive an official ID, which might become a new RFC (e.g. TLS-ALPN-01 RFC 8737 - Automated Certificate Management Environment (ACME) TLS Application-Layer Protocol Negotiation (ALPN) Challenge Extension ).
Modifying the protocol typically entails a new Draft/RFC which obsoletes the old RFC, and then changes it from "proposed standard" (or whatever other classification it matured to) into "Historic". Sometimes the changes are compatible and are an "extension" to the old RFC. I think I've see a few other variants on RFC updates.