salt = make([]byte, h().Size())
}
extractor := hmac.New(h, salt)
+ hmac.MarkAsUsedInHKDF(extractor)
extractor.Write(secret)
return extractor.Sum(nil)
}
func Expand[H fips.Hash](h func() H, pseudorandomKey, info []byte, keyLen int) []byte {
out := make([]byte, 0, keyLen)
expander := hmac.New(h, pseudorandomKey)
+ hmac.MarkAsUsedInHKDF(expander)
var counter uint8
var buf []byte
import (
"bytes"
+ "crypto/internal/boring"
+ "crypto/internal/fips"
"crypto/internal/fips/hkdf"
"crypto/md5"
"crypto/sha1"
hkdf.Key(hash, master, nil, info, limit+1)
}
+func TestFIPSServiceIndicator(t *testing.T) {
+ if boring.Enabled {
+ t.Skip("in BoringCrypto mode HMAC is not from the Go FIPS module")
+ }
+
+ fips.ResetServiceIndicator()
+ hkdf.Key(sha256.New, []byte("YELLOW SUBMARINE"), nil, nil, 32)
+ if !fips.ServiceIndicator() {
+ t.Error("FIPS service indicator should be set")
+ }
+
+ // Key too short.
+ fips.ResetServiceIndicator()
+ hkdf.Key(sha256.New, []byte("key"), nil, nil, 32)
+ if fips.ServiceIndicator() {
+ t.Error("FIPS service indicator should not be set")
+ }
+
+ // Salt and info are short, which is ok, but translates to a short HMAC key.
+ fips.ResetServiceIndicator()
+ hkdf.Key(sha256.New, []byte("YELLOW SUBMARINE"), []byte("salt"), []byte("info"), 32)
+ if !fips.ServiceIndicator() {
+ t.Error("FIPS service indicator should be set")
+ }
+}
+
func Benchmark16ByteMD5Single(b *testing.B) {
benchmarkHKDFSingle(md5.New, 16, b)
}
// copy of the key, but rather the marshaled state of outer/inner after
// opad/ipad has been fed into it.
marshaled bool
+
+ // forHKDF and keyLen are stored to inform the service indicator decision.
+ forHKDF bool
+ keyLen int
}
func (h *HMAC) Sum(in []byte) []byte {
+ // Per FIPS 140-3 IG C.M, key lengths below 112 bits are only allowed for
+ // legacy use (i.e. verification only) and we don't support that. However,
+ // HKDF uses the HMAC key for the salt, which is allowed to be shorter.
+ if h.keyLen < 112/8 && !h.forHKDF {
+ fips.RecordNonApproved()
+ }
+ switch h.inner.(type) {
+ case *sha256.Digest, *sha512.Digest, *sha3.Digest:
+ default:
+ fips.RecordNonApproved()
+ }
+
origLen := len(in)
in = h.inner.Sum(in)
// New returns a new HMAC hash using the given [fips.Hash] type and key.
func New[H fips.Hash](h func() H, key []byte) *HMAC {
- hm := new(HMAC)
+ hm := &HMAC{keyLen: len(key)}
hm.outer = h()
hm.inner = h()
unique := true
if !unique {
panic("crypto/hmac: hash generation function does not produce unique values")
}
- setServiceIndicator(hm.outer, key)
blocksize := hm.inner.BlockSize()
hm.ipad = make([]byte, blocksize)
hm.opad = make([]byte, blocksize)
return hm
}
-func setServiceIndicator(h fips.Hash, key []byte) {
- // Per FIPS 140-3 IG C.M, key lengths below 112 bits are only allowed for
- // legacy use (i.e. verification only) and we don't support that.
- if len(key) < 112/8 {
- fips.RecordNonApproved()
- }
-
- switch h.(type) {
- case *sha256.Digest, *sha512.Digest, *sha3.Digest:
- fips.RecordApproved()
- default:
- fips.RecordNonApproved()
- }
+// MarkAsUsedInHKDF records that this HMAC instance is used as part of HKDF.
+func MarkAsUsedInHKDF(h *HMAC) {
+ h.forHKDF = true
}
// RecordApproved is an internal function that records the use of an approved
// service. It does not override RecordNonApproved calls in the same span.
+//
+// It should be called by exposed functions that perform a whole cryptographic
+// alrgorithm (e.g. by Sum, not by New, unless a cryptographic Instantiate
+// algorithm is performed) and should be called after any checks that may cause
+// the function to error out or panic.
func RecordApproved() {
if getIndicator() == indicatorUnset {
setIndicator(indicatorTrue)