salt = make([]byte, h().Size())
}
extractor := hmac.New(h, salt)
- hmac.MarkAsUsedInHKDF(extractor)
+ hmac.MarkAsUsedInKDF(extractor)
extractor.Write(secret)
return extractor.Sum(nil)
func Expand[H fips140.Hash](h func() H, pseudorandomKey []byte, info string, keyLen int) []byte {
out := make([]byte, 0, keyLen)
expander := hmac.New(h, pseudorandomKey)
- hmac.MarkAsUsedInHKDF(expander)
+ hmac.MarkAsUsedInKDF(expander)
var counter uint8
var buf []byte
return hm
}
-// MarkAsUsedInHKDF records that this HMAC instance is used as part of HKDF.
-func MarkAsUsedInHKDF(h *HMAC) {
+// MarkAsUsedInKDF records that this HMAC instance is used as part of a KDF.
+func MarkAsUsedInKDF(h *HMAC) {
h.forHKDF = true
}
--- /dev/null
+// Copyright 2012 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package pbkdf2
+
+import (
+ "crypto/internal/fips140"
+ _ "crypto/internal/fips140/check"
+ "crypto/internal/fips140/hmac"
+)
+
+func Key[Hash fips140.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) {
+ setServiceIndicator(salt, keyLength)
+
+ prf := hmac.New(h, []byte(password))
+ hmac.MarkAsUsedInKDF(prf)
+ hashLen := prf.Size()
+ numBlocks := (keyLength + hashLen - 1) / hashLen
+
+ var buf [4]byte
+ dk := make([]byte, 0, numBlocks*hashLen)
+ U := make([]byte, hashLen)
+ for block := 1; block <= numBlocks; block++ {
+ // N.B.: || means concatenation, ^ means XOR
+ // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
+ // U_1 = PRF(password, salt || uint(i))
+ prf.Reset()
+ prf.Write(salt)
+ buf[0] = byte(block >> 24)
+ buf[1] = byte(block >> 16)
+ buf[2] = byte(block >> 8)
+ buf[3] = byte(block)
+ prf.Write(buf[:4])
+ dk = prf.Sum(dk)
+ T := dk[len(dk)-hashLen:]
+ copy(U, T)
+
+ // U_n = PRF(password, U_(n-1))
+ for n := 2; n <= iter; n++ {
+ prf.Reset()
+ prf.Write(U)
+ U = U[:0]
+ U = prf.Sum(U)
+ for x := range U {
+ T[x] ^= U[x]
+ }
+ }
+ }
+ return dk[:keyLength], nil
+}
+
+func setServiceIndicator(salt []byte, keyLength int) {
+ // The HMAC construction will handle the hash function considerations for the service
+ // indicator. The remaining PBKDF2 considerations outlined by SP 800-132 pertain to
+ // salt and keyLength.
+
+ // The length of the randomly-generated portion of the salt shall be at least 128 bits.
+ if len(salt) < 128/8 {
+ fips140.RecordNonApproved()
+ }
+
+ // 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 keyLength < 112/8 {
+ fips140.RecordNonApproved()
+ }
+
+ fips140.RecordApproved()
+}
package pbkdf2
import (
- "crypto/hmac"
+ "crypto/internal/fips140/pbkdf2"
"hash"
)
// Using a higher iteration count will increase the cost of an exhaustive
// search but will also make derivation proportionally slower.
func Key[Hash hash.Hash](h func() Hash, password string, salt []byte, iter, keyLength int) ([]byte, error) {
- prf := hmac.New(func() hash.Hash { return h() }, []byte(password))
- hashLen := prf.Size()
- numBlocks := (keyLength + hashLen - 1) / hashLen
-
- var buf [4]byte
- dk := make([]byte, 0, numBlocks*hashLen)
- U := make([]byte, hashLen)
- for block := 1; block <= numBlocks; block++ {
- // N.B.: || means concatenation, ^ means XOR
- // for each block T_i = U_1 ^ U_2 ^ ... ^ U_iter
- // U_1 = PRF(password, salt || uint(i))
- prf.Reset()
- prf.Write(salt)
- buf[0] = byte(block >> 24)
- buf[1] = byte(block >> 16)
- buf[2] = byte(block >> 8)
- buf[3] = byte(block)
- prf.Write(buf[:4])
- dk = prf.Sum(dk)
- T := dk[len(dk)-hashLen:]
- copy(U, T)
-
- // U_n = PRF(password, U_(n-1))
- for n := 2; n <= iter; n++ {
- prf.Reset()
- prf.Write(U)
- U = U[:0]
- U = prf.Sum(U)
- for x := range U {
- T[x] ^= U[x]
- }
- }
- }
- return dk[:keyLength], nil
+ return pbkdf2.Key(h, password, salt, iter, keyLength)
}
import (
"bytes"
+ "crypto/internal/boring"
+ "crypto/internal/fips140"
"crypto/pbkdf2"
"crypto/sha1"
"crypto/sha256"
func BenchmarkHMACSHA256(b *testing.B) {
benchmark(b, sha256.New)
}
+
+func TestPBKDF2ServiceIndicator(t *testing.T) {
+ if boring.Enabled {
+ t.Skip("in BoringCrypto mode PBKDF2 is not from the Go FIPS module")
+ }
+
+ goodSalt := []byte{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10}
+
+ fips140.ResetServiceIndicator()
+ _, err := pbkdf2.Key(sha256.New, "password", goodSalt, 1, 32)
+ if err != nil {
+ t.Error(err)
+ }
+ if !fips140.ServiceIndicator() {
+ t.Error("FIPS service indicator should be set")
+ }
+
+ // Salt too short
+ fips140.ResetServiceIndicator()
+ _, err = pbkdf2.Key(sha256.New, "password", goodSalt[:8], 1, 32)
+ if err != nil {
+ t.Error(err)
+ }
+ if fips140.ServiceIndicator() {
+ t.Error("FIPS service indicator should not be set")
+ }
+
+ // Key length too short
+ fips140.ResetServiceIndicator()
+ _, err = pbkdf2.Key(sha256.New, "password", goodSalt, 1, 10)
+ if err != nil {
+ t.Error(err)
+ }
+ if fips140.ServiceIndicator() {
+ t.Error("FIPS service indicator should not be set")
+ }
+}
< crypto/internal/fips140/sha3
< crypto/internal/fips140/hmac
< crypto/internal/fips140/check
+ < crypto/internal/fips140/pbkdf2
< crypto/internal/fips140/aes
< crypto/internal/fips140/drbg
< crypto/internal/fips140/aes/gcm