golang / go

The Go programming language
https://go.dev
BSD 3-Clause "New" or "Revised" License
123.18k stars 17.56k forks source link

proposal: crypto/hkdf: add package #61477

Open qmuntal opened 1 year ago

qmuntal commented 1 year ago

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 around crypto/hkdf.

I acknowledge that depending on golang.org/x/crypto/hkdf or crypto/hkdf doesn't make much difference in terms of usability, either the x/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:

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:

[!NOTE] See latest API proposal: https://github.com/golang/go/issues/61477#issuecomment-2353412350.

// Expand returns a Reader, from which keys can be read, using the given
// pseudorandom key and optional context info, skipping the extraction step.
//
// 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 New instead.
func Expand(hash func() hash.Hash, pseudorandomKey, info []byte) io.Reader

// 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 New instead.
func Extract(hash func() hash.Hash, secret, salt []byte) []byte

// New returns a Reader, from which keys can be read, using the given hash,
// secret, salt and context info. Salt and info can be nil.
func New(hash func() hash.Hash, secret, salt, info []byte) io.Reader

Related to #65269.

@golang/security

qmuntal commented 1 year ago

@rolandshoemaker @FiloSottile

qmuntal commented 1 week ago

I've been thinking a bit more about this. The x/crypto/hkdf API might be over-complicated given that most of hkdf.New and hkdf.Expand uses ends up with a single Read or io.ReadFull call.

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)
ericlagergren commented 1 week ago

@qmuntal Getting rid of the io.Reader is a good change, but not being able to generate a key into an existing slice is rather unfortunate.

qmuntal commented 1 week ago

Getting rid of the io.Reader is a good change, but not being able to generate a key into an existing slice is rather unfortunate.

I proposed the keyLen instead of passing am existing slice for symmetry with other kdf packages in x/crypto, but passing an slice instead also works for me.