]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/internal/fips/pbkdf2: fips import pbkdf2
authorDaniel McCarney <daniel@binaryparadox.net>
Thu, 14 Nov 2024 19:09:13 +0000 (14:09 -0500)
committerGopher Robot <gobot@golang.org>
Fri, 22 Nov 2024 00:00:19 +0000 (00:00 +0000)
This commit lifts the internals of crypto/pbkdf2 into
crypto/internal/fips140/pbkdf2, in the FIPS module. The code
remains unchanged except for the following adjustments:

* The hash and hmac imports now come from the FIPS equivalents.
* The FIPS service indicator status is set based on the SP 800-132
  requirements for PBKDF2.

For #69536

Change-Id: I61f47a652cef10505a5b40a70be5240b161a97ba
Reviewed-on: https://go-review.googlesource.com/c/go/+/619236
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Derek Parker <parkerderek86@gmail.com>
Reviewed-by: Filippo Valsorda <filippo@golang.org>
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Roland Shoemaker <roland@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
src/crypto/internal/fips140/hkdf/hkdf.go
src/crypto/internal/fips140/hmac/hmac.go
src/crypto/internal/fips140/pbkdf2/pbkdf2.go [new file with mode: 0644]
src/crypto/pbkdf2/pbkdf2.go
src/crypto/pbkdf2/pbkdf2_test.go
src/go/build/deps_test.go

index 6ddae5c3f2febccd5b7440e26c7a542071e1d2e2..e612fbbc21c87f42854f4cae2bcc6d0ec1e1b377 100644 (file)
@@ -17,7 +17,7 @@ func Extract[H fips140.Hash](h func() H, secret, salt []byte) []byte {
                salt = make([]byte, h().Size())
        }
        extractor := hmac.New(h, salt)
-       hmac.MarkAsUsedInHKDF(extractor)
+       hmac.MarkAsUsedInKDF(extractor)
        extractor.Write(secret)
 
        return extractor.Sum(nil)
@@ -26,7 +26,7 @@ func Extract[H fips140.Hash](h func() H, secret, salt []byte) []byte {
 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
 
index 320d78f2685574e504bd5d9b336b229c3a5fc7a0..5a588d7c260197f3c10f2d0e8ea7b44b48e24a4b 100644 (file)
@@ -166,7 +166,7 @@ func New[H fips140.Hash](h func() H, key []byte) *HMAC {
        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
 }
diff --git a/src/crypto/internal/fips140/pbkdf2/pbkdf2.go b/src/crypto/internal/fips140/pbkdf2/pbkdf2.go
new file mode 100644 (file)
index 0000000..3d4e385
--- /dev/null
@@ -0,0 +1,70 @@
+// 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()
+}
index 0887365388f18e7c9d57cc0df91e86a93db55438..e6fef68b298cd812abb3890708ebd7b6a6e3bb85 100644 (file)
@@ -19,7 +19,7 @@ pbkdf2.Key.
 package pbkdf2
 
 import (
-       "crypto/hmac"
+       "crypto/internal/fips140/pbkdf2"
        "hash"
 )
 
@@ -40,38 +40,5 @@ import (
 // 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)
 }
index ecce26f8ba079875441911b67445171317c0838f..03980c7e54d3be59a85c9569ae358f6a32069056 100644 (file)
@@ -6,6 +6,8 @@ package pbkdf2_test
 
 import (
        "bytes"
+       "crypto/internal/boring"
+       "crypto/internal/fips140"
        "crypto/pbkdf2"
        "crypto/sha1"
        "crypto/sha256"
@@ -182,3 +184,40 @@ func BenchmarkHMACSHA1(b *testing.B) {
 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")
+       }
+}
index 662bb594396f72e61b9a7d32bd41468f1ab507b0..4ff73b08c3ba80af3bc7f52675531e7de61da0ba 100644 (file)
@@ -467,6 +467,7 @@ var depsRules = `
        < 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