-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
crypto/hkdf: add package #61477
Comments
I've been thinking a bit more about this. The A simpler and easier to use API could look like this: // Expand derives a key from the given hash, pseudorandomKey, and optional context info,
// returning a []byte of length keyLen that can be used as cryptographic key.
// The extraction step is skipped.
//
// The pseudorandomKey should have been generated by Extract, or be a uniformly
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
// 3.3. Most common scenarios will want to use Key instead.
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte, keyLen int) ([]byte, error)
// Extract generates a pseudorandom key for use with Expand from an input
// secret and an optional independent salt.
//
// Only use this function if you need to reuse the extracted key with multiple
// Expand invocations and different context values. Most common scenarios,
// including the generation of multiple keys, should use Key instead.
func Extract(hash func() hash.Hash, secret, salt []byte) []byte
// Key derives a key from the given hash, secret, salt and context info,
// returning a []byte of length keyLen that can be used as cryptographic key.
// Salt and info can be nil.
func Key(hash func() hash.Hash, secret, salt, info []byte, keyLen int) ([]byte, error) |
@qmuntal Getting rid of the |
I proposed the |
I was going to suggest using the mid-stack inlining trick to let us use the nice API (accept |
Yep, this is a known long-standing limitation of the compiler: #29095. @rolandshoemaker @FiloSottile what do your think about this API proposal: #61477 (comment) |
Re: moving the package, we'll definitely want to do that in Go 1.24 for #69536, although I can't promise we'll want to expand the Go+BoringCrypto mode to cover it. On removing the
(2) is a problem because we'd like to make moved x/crypto packages simple wrappers around the standard library ones. (1) depends on how applications have been using HKDF. Have they taken to use it as a XOF? As you said, the API has been unchanged for many years... A compromise could be to leave The allocation can actually be largely mitigated by doing something like the following. I haven't tested but I am confident that some variation will work.
While we are making changes, I think I'd like to make info and maybe salt into strings. |
Do we still want to return an |
Yes, strong +1. I think all new crypto APIs we add should return concrete types. |
That's fine. Let's focus on the new API for now.
On GitHub, There are 435 occurrences of
Doing a quick GitHub sampling I would say that 99% of
If we drop the Having said this, I'm not opposed to keeping the |
I think it's a good idea, but it would be unfortunate if it ends up requiring an additional allocation for non-constant info parameters. Actually, something like [[]byte]byte would be wonderful for implementing HPKE without allocating. |
Sample size n=1, but I've never once needed nor wanted to use HKDF as an XOF. That would raise some eyebrows. Also, I can't immediately think of any major crypto libraries that have the same behavior as Go here. |
The max output of Expand is Still, the sampling is pretty compelling. |
Change https://go.dev/cl/621275 mentions this issue: |
This proposal has been added to the active column of the proposals project |
Replacing the I propose we simply don't reimplement |
Should it be generic over |
The issue with that is that applications that use
Yeah, maybe! I ended up making the
Sounds like we are converging on #61477 (comment), with three open questions (two from above, the last one new):
|
Expand needs an error because HKDF has a maximum output size. |
I would add errors to all the functions defined here. It is more future proof and it would play better with the OpenSSL/CNG bindings I maintain, which are more likely to return an error. In fact,
I see how this is a desirable requirement. We can always |
HKDF salts are somewhat different from PBKDF2 salts, and often misunderstood, so they are more often strings than their PBKDF2 counterparts, which instead are almost always random. (The difference is due to the fact that HKDF operates already on high-entropy inputs, and only needs the salt for domain separation and to make some proofs cleaner, while PBKDF2 is actually defending against precomputation.) Anyway, info is more string-y than salt anyway, the usual pattern is to make it just a label for the purpose of the key, so let's make that a string and salt a byte slice, so it's consistent.
|
Oh, and sure, let's return errors.
|
How can |
In FIPS-only mode if the secret is too short. In general I don’t want us to add error returns just for the benefit of FIPS-only mode, but since the other two already return errors for keyLen out of bounds, making them consistent feels fine. |
Based on the discussion above, this proposal seems like a likely accept. The proposal is to add a new // Extract generates a pseudorandom key for use with [Expand] from an input
// secret and an optional independent salt.
//
// Only use this function if you need to reuse the extracted key with multiple
// Expand invocations and different context values. Most common scenarios,
// including the generation of multiple keys, should use [Key] instead.
func Extract[Hash hash.Hash](h func() Hash, secret, salt []byte) ([]byte, error)
// Expand derives a key from the given hash, key, and optional context info,
// returning a []byte of length keyLength that can be used as cryptographic key.
// The extraction step is skipped.
//
// The key should have been generated by [Extract], or be a uniformly
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
// 3.3. Most common scenarios will want to use [Key] instead.
func Expand[Hash hash.Hash](h func() Hash, key []byte, info string, keyLength int) ([]byte, error)
// Key derives a key from the given hash, secret, salt and context info,
// returning a []byte of length keyLength that can be used as cryptographic key.
// Salt and info can be nil.
func Key[Hash hash.Hash](h func() Hash, secret, salt []byte, info string, keyLength int) ([]byte, error) |
Tests imported from x/crypto, but the actual implementation was simpler to implement ex-novo with a #61477-like API. Updates #61477 For #69536 Change-Id: I5a9e8a71d8abd5b2aa6b74e73bf7f631ed0115cd Reviewed-on: https://go-review.googlesource.com/c/go/+/621275 Reviewed-by: Dmitri Shuralyov <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Daniel McCarney <[email protected]> Reviewed-by: Russ Cox <[email protected]>
The problem with adding an error return to a function like Extract which is expected to ~never fail is that it will be very tempting to ignore the error when calling it.
Especially if the only foreseeable error is "secret too small". That sounds like programmer error and it seems more appropriate to panic in that case than allow the program to continue. |
Change https://go.dev/cl/630296 mentions this issue: |
Hmm, not really, the secret could come from disk or network. I don't think this is the right place for a panic. I do hear you about ignoring it being tempting, but how are Key and Expand different? If you put a fixed value as keyLength, they have the same failure mode as Extract. There are good arguments to make all three return errors or not, but I don't think there's a strong argument to not make them consistent. |
I would argue that they are not, and that the error return isn't carrying its weight there either. This seems supported by the fact that the initial implementation of crypto/internal/fips/hkdf did not require any error returns. |
No change in consensus, so accepted. 🎉 The proposal is to add a new // Extract generates a pseudorandom key for use with [Expand] from an input
// secret and an optional independent salt.
//
// Only use this function if you need to reuse the extracted key with multiple
// Expand invocations and different context values. Most common scenarios,
// including the generation of multiple keys, should use [Key] instead.
func Extract[Hash hash.Hash](h func() Hash, secret, salt []byte) ([]byte, error)
// Expand derives a key from the given hash, key, and optional context info,
// returning a []byte of length keyLength that can be used as cryptographic key.
// The extraction step is skipped.
//
// The key should have been generated by [Extract], or be a uniformly
// random or pseudorandom cryptographically strong key. See RFC 5869, Section
// 3.3. Most common scenarios will want to use [Key] instead.
func Expand[Hash hash.Hash](h func() Hash, key []byte, info string, keyLength int) ([]byte, error)
// Key derives a key from the given hash, secret, salt and context info,
// returning a []byte of length keyLength that can be used as cryptographic key.
// Salt and info can be nil.
func Key[Hash hash.Hash](h func() Hash, secret, salt []byte, info string, keyLength int) ([]byte, error) |
This commit imports the x/crypto/hkdf package as a public crypto package based on the linked proposal. Since we've already implemented this internal to the FIPS boundary (mod some small changes based on the proposal discussion) this largely defers to that implementation. Updates #61477 Change-Id: Ie3dcee75314dfbe22eec8b31c43c926fe80637bb Reviewed-on: https://go-review.googlesource.com/c/go/+/630296 Reviewed-by: Filippo Valsorda <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Reviewed-by: Russ Cox <[email protected]> Auto-Submit: Filippo Valsorda <[email protected]>
I think this can be closed since 630296 landed. I used the wrong linkage in the CR description, sorry! |
Change https://go.dev/cl/634095 mentions this issue: |
For #61477 Change-Id: I3d3ebf573a21f1f56edfffb3fea53c0b5cbfccd8 Reviewed-on: https://go-review.googlesource.com/c/go/+/634095 Reviewed-by: Roland Shoemaker <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]>
Important
Nov 20, 2024: The latest version of the proposal is here
I propose to move the golang.org/x/crypto/hkdf package into the standard library with the name
crypto/hkdf
.golang.org/x/crypto/hkdf
would then be updated to just be a wrapper aroundcrypto/hkdf
.I acknowledge that depending on
golang.org/x/crypto/hkdf
orcrypto/hkdf
doesn't make much difference in terms of usability, either thex/crypto
package and the standard library promise backwards compatibility and are respected by the Go community. Yet, doing this move will bring two main benefits for the Go standard library and for the niche of users requiring FIPS 140 compliance:crypto/tls
usesgolang.org/x/crypto/hkdf
to implement TLS 1.3, and there are some Go forks out there that either already provide FIPS compliant TLS 1.3 via OpenSSL, or plan to do so in the near term. I suppose that Google will eventually also provide it, but this is out of this proposal. Adding this support would be much easier ifcrypto/hkdf
was part of the standard library, in which case it could be patched to forward calls tocrypto/internal/boring
as needed.hkdf
is widely used outside the standard library. Users depending on it that also require FIPS 140 compliance will benefit from having it in the standard library as a package backed by BoringCrypto/OpenSSL/CNG, etc.Worth noting that
golang.org/x/crypto/hkdf
API has remained the same for more than 5 years, and that its git log only contains 4 commits since it was added in 2014. It seems to already be in good shape, so I don't expect that moving it to the standard library would require much additional maintenance effort.For completeness, this is the current
golang.org/x/crypto/hkdf
API that I'm proposing to add to the standard library:Related to #65269.
@golang/security
The text was updated successfully, but these errors were encountered: