crypto/internal/fips/drbg: implement FIPS-mode randomness generation

For #69536

Change-Id: I5bc83360fcd9114cc76314c3570977e9811fd1c6
Reviewed-on: https://go-review.googlesource.com/c/go/+/624978
Auto-Submit: Filippo Valsorda <filippo@golang.org>
Reviewed-by: Dmitri Shuralyov <dmitshur@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Daniel McCarney <daniel@binaryparadox.net>
Reviewed-by: Roland Shoemaker <roland@golang.org>
This commit is contained in:
Filippo Valsorda 2024-11-04 15:33:00 +01:00 committed by Gopher Robot
parent e67037fb27
commit 0d8b96de05
5 changed files with 109 additions and 3 deletions

View File

@ -0,0 +1,28 @@
// Copyright 2024 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 entropy provides the passive entropy source for the FIPS 140-3
// module. It is only used in FIPS mode by [crypto/internal/fips/drbg.Read].
//
// This complies with IG 9.3.A, Additional Comment 12, which until January 1,
// 2026 allows new modules to meet an [earlier version] of Resolution 2(b):
// "A software module that contains an approved DRBG that receives a LOAD
// command (or its logical equivalent) with entropy obtained from [...] inside
// the physical perimeter of the operational environment of the module [...]."
//
// Distributions that have their own SP 800-90B entropy source should replace
// this package with their own implementation.
//
// [earlier version]: https://csrc.nist.gov/CSRC/media/Projects/cryptographic-module-validation-program/documents/IG%209.3.A%20Resolution%202b%5BMarch%2026%202024%5D.pdf
package entropy
import "crypto/internal/sysrand"
// Depleted notifies the entropy source that the entropy in the module is
// "depleted" and provides the callback for the LOAD command.
func Depleted(LOAD func(*[48]byte)) {
var entropy [48]byte
sysrand.Read(entropy[:])
LOAD(&entropy)
}

View File

@ -0,0 +1,58 @@
// Copyright 2024 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 drbg
import (
"crypto/internal/entropy"
"crypto/internal/fips"
"crypto/internal/sysrand"
"sync"
)
var mu sync.Mutex
var drbg *Counter
// Read fills b with cryptographically secure random bytes. In FIPS mode, it
// uses an SP 800-90A Rev. 1 Deterministic Random Bit Generator (DRBG).
// Otherwise, it uses the operating system's random number generator.
func Read(b []byte) {
if !fips.Enabled {
sysrand.Read(b)
return
}
// At every read, 128 random bits from the operating system are mixed as
// additional input, to make the output as strong as non-FIPS randomness.
// This is not credited as entropy for FIPS purposes, as allowed by Section
// 8.7.2: "Note that a DRBG does not rely on additional input to provide
// entropy, even though entropy could be provided in the additional input".
additionalInput := new([SeedSize]byte)
sysrand.Read(additionalInput[:16])
mu.Lock()
defer mu.Unlock()
if drbg == nil {
entropy.Depleted(func(seed *[48]byte) {
drbg = NewCounter(seed)
})
}
for len(b) > 0 {
size := min(len(b), maxRequestSize)
if reseedRequired := drbg.Generate(b[:size], additionalInput); reseedRequired {
// See SP 800-90A Rev. 1, Section 9.3.1, Steps 6-8, as explained in
// Section 9.3.2: if Generate reports a reseed is required, the
// additional input is passed to Reseed along with the entropy and
// then nulled before the next Generate call.
entropy.Depleted(func(seed *[48]byte) {
drbg.Reseed(seed, additionalInput)
})
additionalInput = nil
continue
}
b = b[size:]
}
}

View File

@ -0,0 +1,9 @@
// Copyright 2024 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 fips
import "internal/godebug"
var Enabled = godebug.New("#fips140").Value() == "on"

View File

@ -8,6 +8,8 @@ package rand
import (
"crypto/internal/boring"
"crypto/internal/fips"
"crypto/internal/fips/drbg"
"crypto/internal/sysrand"
"io"
_ "unsafe"
@ -23,6 +25,9 @@ import (
// - On Windows, Reader uses the ProcessPrng API.
// - On js/wasm, Reader uses the Web Crypto API.
// - On wasip1/wasm, Reader uses random_get.
//
// In FIPS 140-3 mode, the output passes through an SP 800-90A Rev. 1
// Deterministric Random Bit Generator (DRBG).
var Reader io.Reader
func init() {
@ -37,7 +42,11 @@ type reader struct{}
func (r *reader) Read(b []byte) (n int, err error) {
boring.Unreachable()
sysrand.Read(b)
if fips.Enabled {
drbg.Read(b)
} else {
sysrand.Read(b)
}
return len(b), nil
}

View File

@ -441,13 +441,15 @@ var depsRules = `
< net/mail;
STR < crypto/internal/impl;
OS < crypto/internal/sysrand;
OS < crypto/internal/sysrand
< crypto/internal/entropy;
# FIPS is the FIPS 140 module.
# It must not depend on external crypto packages.
# Internal packages imported by FIPS might need to retain
# backwards compatibility with older versions of the module.
STR, crypto/internal/impl, crypto/internal/sysrand
STR, crypto/internal/impl, crypto/internal/entropy
< crypto/internal/fips
< crypto/internal/fips/alias
< crypto/internal/fips/subtle