mirror of
https://github.com/golang/go.git
synced 2025-05-07 08:32:59 +00:00
crypto/internal/fips: add service indicator mechanism
Placed the fipsIndicator field in some 64-bit alignment padding in the g struct to avoid growing per-goroutine memory requirements on 64-bit targets. Fixes #69911 Updates #69536 Change-Id: I176419d0e3814574758cb88a47340a944f405604 Reviewed-on: https://go-review.googlesource.com/c/go/+/620795 Reviewed-by: Roland Shoemaker <roland@golang.org> Reviewed-by: Daniel McCarney <daniel@binaryparadox.net> Reviewed-by: Michael Pratt <mpratt@google.com> Auto-Submit: Filippo Valsorda <filippo@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Derek Parker <parkerderek86@gmail.com>
This commit is contained in:
parent
ba1caa8b30
commit
f0b51a2099
@ -155,14 +155,13 @@ func setServiceIndicator(h fips.Hash, key []byte) {
|
|||||||
// Per FIPS 140-3 IG C.M, key lengths below 112 bits are only allowed for
|
// 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.
|
// legacy use (i.e. verification only) and we don't support that.
|
||||||
if len(key) < 112/8 {
|
if len(key) < 112/8 {
|
||||||
return
|
fips.RecordNonApproved()
|
||||||
}
|
}
|
||||||
|
|
||||||
switch h.(type) {
|
switch h.(type) {
|
||||||
case *sha256.Digest, *sha512.Digest, *sha3.Digest:
|
case *sha256.Digest, *sha512.Digest, *sha3.Digest:
|
||||||
|
fips.RecordApproved()
|
||||||
default:
|
default:
|
||||||
return
|
fips.RecordNonApproved()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(fips): set service indicator.
|
|
||||||
}
|
}
|
||||||
|
57
src/crypto/internal/fips/indicator.go
Normal file
57
src/crypto/internal/fips/indicator.go
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
// 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 _ "unsafe" // for go:linkname
|
||||||
|
|
||||||
|
// The service indicator lets users of the module query whether invoked services
|
||||||
|
// are approved. Three states are stored in a per-goroutine value by the
|
||||||
|
// runtime. The indicator starts at indicatorUnset after a reset. Invoking an
|
||||||
|
// approved service transitions to indicatorTrue. Invoking a non-approved
|
||||||
|
// service transitions to indicatorFalse, and it can't leave that state until a
|
||||||
|
// reset. The idea is that functions can "delegate" checks to inner functions,
|
||||||
|
// and if there's anything non-approved in the stack, the final result is
|
||||||
|
// negative. Finally, we expose indicatorUnset as negative to the user, so that
|
||||||
|
// we don't need to explicitly annotate fully non-approved services.
|
||||||
|
|
||||||
|
//go:linkname getIndicator
|
||||||
|
func getIndicator() uint8
|
||||||
|
|
||||||
|
//go:linkname setIndicator
|
||||||
|
func setIndicator(uint8)
|
||||||
|
|
||||||
|
const (
|
||||||
|
indicatorUnset uint8 = iota
|
||||||
|
indicatorFalse
|
||||||
|
indicatorTrue
|
||||||
|
)
|
||||||
|
|
||||||
|
// ResetServiceIndicator clears the service indicator for the running goroutine.
|
||||||
|
func ResetServiceIndicator() {
|
||||||
|
setIndicator(indicatorUnset)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServiceIndicator returns true if and only if all services invoked by this
|
||||||
|
// goroutine since the last ResetServiceIndicator call are approved.
|
||||||
|
//
|
||||||
|
// If ResetServiceIndicator was not called before by this goroutine, its return
|
||||||
|
// value is undefined.
|
||||||
|
func ServiceIndicator() bool {
|
||||||
|
return getIndicator() == indicatorTrue
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordApproved is an internal function that records the use of an approved
|
||||||
|
// service. It does not override RecordNonApproved calls in the same span.
|
||||||
|
func RecordApproved() {
|
||||||
|
if getIndicator() == indicatorUnset {
|
||||||
|
setIndicator(indicatorTrue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecordNonApproved is an internal function that records the use of a
|
||||||
|
// non-approved service. It overrides any RecordApproved calls in the same span.
|
||||||
|
func RecordNonApproved() {
|
||||||
|
setIndicator(indicatorFalse)
|
||||||
|
}
|
76
src/crypto/internal/fips/indicator_test.go
Normal file
76
src/crypto/internal/fips/indicator_test.go
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// 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_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/internal/fips"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIndicator(t *testing.T) {
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
if fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be false if no calls are made")
|
||||||
|
}
|
||||||
|
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordApproved()
|
||||||
|
if !fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be true if RecordApproved is called")
|
||||||
|
}
|
||||||
|
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordApproved()
|
||||||
|
fips.RecordApproved()
|
||||||
|
if !fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be true if RecordApproved is called multiple times")
|
||||||
|
}
|
||||||
|
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordNonApproved()
|
||||||
|
if fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be false if RecordNonApproved is called")
|
||||||
|
}
|
||||||
|
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordApproved()
|
||||||
|
fips.RecordNonApproved()
|
||||||
|
if fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be false if both RecordApproved and RecordNonApproved are called")
|
||||||
|
}
|
||||||
|
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordNonApproved()
|
||||||
|
fips.RecordApproved()
|
||||||
|
if fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be false if both RecordNonApproved and RecordApproved are called")
|
||||||
|
}
|
||||||
|
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordNonApproved()
|
||||||
|
done := make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordApproved()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
<-done
|
||||||
|
if fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be false if RecordApproved is called in a different goroutine")
|
||||||
|
}
|
||||||
|
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordApproved()
|
||||||
|
done = make(chan struct{})
|
||||||
|
go func() {
|
||||||
|
fips.ResetServiceIndicator()
|
||||||
|
fips.RecordNonApproved()
|
||||||
|
close(done)
|
||||||
|
}()
|
||||||
|
<-done
|
||||||
|
if !fips.ServiceIndicator() {
|
||||||
|
t.Error("indicator should be true if RecordNonApproved is called in a different goroutine")
|
||||||
|
}
|
||||||
|
}
|
@ -7,6 +7,7 @@
|
|||||||
package sha256
|
package sha256
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/internal/fips"
|
||||||
"errors"
|
"errors"
|
||||||
"internal/byteorder"
|
"internal/byteorder"
|
||||||
)
|
)
|
||||||
@ -181,6 +182,7 @@ func (d *Digest) Write(p []byte) (nn int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Digest) Sum(in []byte) []byte {
|
func (d *Digest) Sum(in []byte) []byte {
|
||||||
|
fips.RecordApproved()
|
||||||
// Make a copy of d so that caller can keep writing and summing.
|
// Make a copy of d so that caller can keep writing and summing.
|
||||||
d0 := *d
|
d0 := *d
|
||||||
hash := d0.checkSum()
|
hash := d0.checkSum()
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
package sha3
|
package sha3
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/internal/fips"
|
||||||
"crypto/internal/fips/subtle"
|
"crypto/internal/fips/subtle"
|
||||||
"errors"
|
"errors"
|
||||||
)
|
)
|
||||||
@ -144,7 +145,11 @@ func (d *Digest) readGeneric(out []byte) (n int, err error) {
|
|||||||
|
|
||||||
// Sum appends the current hash to b and returns the resulting slice.
|
// Sum appends the current hash to b and returns the resulting slice.
|
||||||
// It does not change the underlying hash state.
|
// It does not change the underlying hash state.
|
||||||
func (d *Digest) Sum(b []byte) []byte { return d.sum(b) }
|
func (d *Digest) Sum(b []byte) []byte {
|
||||||
|
fips.RecordApproved()
|
||||||
|
return d.sum(b)
|
||||||
|
}
|
||||||
|
|
||||||
func (d *Digest) sumGeneric(b []byte) []byte {
|
func (d *Digest) sumGeneric(b []byte) []byte {
|
||||||
if d.state != spongeAbsorbing {
|
if d.state != spongeAbsorbing {
|
||||||
panic("sha3: Sum after Read")
|
panic("sha3: Sum after Read")
|
||||||
|
@ -6,6 +6,7 @@ package sha3
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/internal/fips"
|
||||||
"errors"
|
"errors"
|
||||||
"internal/byteorder"
|
"internal/byteorder"
|
||||||
"math/bits"
|
"math/bits"
|
||||||
@ -71,6 +72,7 @@ func (s *SHAKE) Sum(in []byte) []byte { return s.d.Sum(in) }
|
|||||||
func (s *SHAKE) Write(p []byte) (n int, err error) { return s.d.Write(p) }
|
func (s *SHAKE) Write(p []byte) (n int, err error) { return s.d.Write(p) }
|
||||||
|
|
||||||
func (s *SHAKE) Read(out []byte) (n int, err error) {
|
func (s *SHAKE) Read(out []byte) (n int, err error) {
|
||||||
|
fips.RecordApproved()
|
||||||
// Note that read is not exposed on Digest since SHA-3 does not offer
|
// Note that read is not exposed on Digest since SHA-3 does not offer
|
||||||
// variable output length. It is only used internally by Sum.
|
// variable output length. It is only used internally by Sum.
|
||||||
return s.d.read(out)
|
return s.d.read(out)
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
package sha512
|
package sha512
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/internal/fips"
|
||||||
"errors"
|
"errors"
|
||||||
"internal/byteorder"
|
"internal/byteorder"
|
||||||
)
|
)
|
||||||
@ -251,6 +252,7 @@ func (d *Digest) Write(p []byte) (nn int, err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Digest) Sum(in []byte) []byte {
|
func (d *Digest) Sum(in []byte) []byte {
|
||||||
|
fips.RecordApproved()
|
||||||
// Make a copy of d so that caller can keep writing and summing.
|
// Make a copy of d so that caller can keep writing and summing.
|
||||||
d0 := new(Digest)
|
d0 := new(Digest)
|
||||||
*d0 = *d
|
*d0 = *d
|
||||||
|
@ -724,3 +724,13 @@ func reflect_addReflectOff(ptr unsafe.Pointer) int32 {
|
|||||||
reflectOffsUnlock()
|
reflectOffsUnlock()
|
||||||
return id
|
return id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//go:linkname fips_getIndicator crypto/internal/fips.getIndicator
|
||||||
|
func fips_getIndicator() uint8 {
|
||||||
|
return getg().fipsIndicator
|
||||||
|
}
|
||||||
|
|
||||||
|
//go:linkname fips_setIndicator crypto/internal/fips.setIndicator
|
||||||
|
func fips_setIndicator(indicator uint8) {
|
||||||
|
getg().fipsIndicator = indicator
|
||||||
|
}
|
||||||
|
@ -466,6 +466,7 @@ type g struct {
|
|||||||
trackingStamp int64 // timestamp of when the G last started being tracked
|
trackingStamp int64 // timestamp of when the G last started being tracked
|
||||||
runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking
|
runnableTime int64 // the amount of time spent runnable, cleared when running, only used when tracking
|
||||||
lockedm muintptr
|
lockedm muintptr
|
||||||
|
fipsIndicator uint8
|
||||||
sig uint32
|
sig uint32
|
||||||
writebuf []byte
|
writebuf []byte
|
||||||
sigcode0 uintptr
|
sigcode0 uintptr
|
||||||
|
@ -20,7 +20,7 @@ func TestSizeof(t *testing.T) {
|
|||||||
_32bit uintptr // size on 32bit platforms
|
_32bit uintptr // size on 32bit platforms
|
||||||
_64bit uintptr // size on 64bit platforms
|
_64bit uintptr // size on 64bit platforms
|
||||||
}{
|
}{
|
||||||
{runtime.G{}, 272, 432}, // g, but exported for testing
|
{runtime.G{}, 276, 432}, // g, but exported for testing
|
||||||
{runtime.Sudog{}, 56, 88}, // sudog, but exported for testing
|
{runtime.Sudog{}, 56, 88}, // sudog, but exported for testing
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user