aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorFilippo Valsorda <filippo@golang.org>2025-10-29 13:05:19 +0100
committerGopher Robot <gobot@golang.org>2025-11-03 07:14:16 -0800
commit00ee1860ce877ec9e2e323d7e48dee083e0902bd (patch)
treec3985753e112120527a399b956cdd424fcffd465 /src
parent388c41c412c24b751c8c09465787ae79bceca9c7 (diff)
downloadgo-00ee1860ce877ec9e2e323d7e48dee083e0902bd.tar.xz
crypto/internal/constanttime: expose intrinsics to the FIPS 140-3 packages
Intrinsifying things inside the module (crypto/internal/fips140/subtle) is asking for trouble, as the import paths are rewritten by the GOFIPS140 mechanism, and we might have to support multiple modules in the future. Importing crypto/subtle from inside a FIPS 140-3 module is not allowed, and is basically asking for circular dependencies. Instead, break off the intrinsics into their own package (crypto/internal/constanttime), and keep the byte slice operations in crypto/internal/fips140/subtle. crypto/subtle then becomes a thin dispatch layer. Change-Id: I6a6a6964cd5cb5ad06e9d1679201447f5a811da4 Reviewed-on: https://go-review.googlesource.com/c/go/+/716120 Reviewed-by: Keith Randall <khr@google.com> Reviewed-by: Michael Knyszek <mknyszek@google.com> Reviewed-by: Keith Randall <khr@golang.org> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Auto-Submit: Filippo Valsorda <filippo@golang.org> Reviewed-by: Jorropo <jorropo.pgm@gmail.com>
Diffstat (limited to 'src')
-rw-r--r--src/cmd/compile/internal/ssagen/intrinsics.go10
-rw-r--r--src/cmd/compile/internal/ssagen/intrinsics_test.go40
-rw-r--r--src/crypto/internal/constanttime/constant_time.go42
-rw-r--r--src/crypto/internal/fips140/edwards25519/tables.go8
-rw-r--r--src/crypto/internal/fips140/nistec/generate.go4
-rw-r--r--src/crypto/internal/fips140/nistec/p224.go4
-rw-r--r--src/crypto/internal/fips140/nistec/p256.go10
-rw-r--r--src/crypto/internal/fips140/nistec/p384.go4
-rw-r--r--src/crypto/internal/fips140/nistec/p521.go4
-rw-r--r--src/crypto/internal/fips140/rsa/pkcs1v22.go13
-rw-r--r--src/crypto/internal/fips140/subtle/constant_time.go25
-rw-r--r--src/crypto/internal/fips140deps/fipsdeps_test.go3
-rw-r--r--src/crypto/subtle/constant_time.go40
-rw-r--r--src/go/build/deps_test.go3
14 files changed, 110 insertions, 100 deletions
diff --git a/src/cmd/compile/internal/ssagen/intrinsics.go b/src/cmd/compile/internal/ssagen/intrinsics.go
index bf9e71c170..190c4840ce 100644
--- a/src/cmd/compile/internal/ssagen/intrinsics.go
+++ b/src/cmd/compile/internal/ssagen/intrinsics.go
@@ -1603,10 +1603,10 @@ func initIntrinsics(cfg *intrinsicBuildConfig) {
},
sys.AMD64)
- /******** crypto/subtle ********/
- // We implement a superset of the ConstantTimeSelect promise:
- // ConstantTimeSelect returns x if v != 0 and y if v == 0.
- add("crypto/subtle", "ConstantTimeSelect",
+ /******** crypto/internal/constanttime ********/
+ // We implement a superset of the Select promise:
+ // Select returns x if v != 0 and y if v == 0.
+ add("crypto/internal/constanttime", "Select",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
v, x, y := args[0], args[1], args[2]
@@ -1627,7 +1627,7 @@ func initIntrinsics(cfg *intrinsicBuildConfig) {
return s.newValue3(ssa.OpCondSelect, types.Types[types.TINT], x, y, check)
},
sys.ArchAMD64, sys.ArchARM64, sys.ArchLoong64, sys.ArchPPC64, sys.ArchPPC64LE, sys.ArchWasm) // all with CMOV support.
- add("crypto/subtle", "constantTimeBoolToUint8",
+ add("crypto/internal/constanttime", "boolToUint8",
func(s *state, n *ir.CallExpr, args []*ssa.Value) *ssa.Value {
return s.newValue1(ssa.OpCvtBoolToUint8, types.Types[types.TUINT8], args[0])
},
diff --git a/src/cmd/compile/internal/ssagen/intrinsics_test.go b/src/cmd/compile/internal/ssagen/intrinsics_test.go
index 9311f84345..782426215c 100644
--- a/src/cmd/compile/internal/ssagen/intrinsics_test.go
+++ b/src/cmd/compile/internal/ssagen/intrinsics_test.go
@@ -42,7 +42,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"386", "math/bits", "TrailingZeros8"}: struct{}{},
{"386", "runtime", "KeepAlive"}: struct{}{},
{"386", "runtime", "slicebytetostringtmp"}: struct{}{},
- {"386", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"386", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"amd64", "internal/runtime/atomic", "And"}: struct{}{},
{"amd64", "internal/runtime/atomic", "And32"}: struct{}{},
{"amd64", "internal/runtime/atomic", "And64"}: struct{}{},
@@ -189,8 +189,8 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"amd64", "sync/atomic", "SwapUint32"}: struct{}{},
{"amd64", "sync/atomic", "SwapUint64"}: struct{}{},
{"amd64", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"amd64", "crypto/subtle", "ConstantTimeSelect"}: struct{}{},
- {"amd64", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"amd64", "crypto/internal/constanttime", "Select"}: struct{}{},
+ {"amd64", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"arm", "internal/runtime/sys", "Bswap32"}: struct{}{},
{"arm", "internal/runtime/sys", "Bswap64"}: struct{}{},
{"arm", "internal/runtime/sys", "GetCallerPC"}: struct{}{},
@@ -219,7 +219,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"arm", "math/bits", "TrailingZeros8"}: struct{}{},
{"arm", "runtime", "KeepAlive"}: struct{}{},
{"arm", "runtime", "slicebytetostringtmp"}: struct{}{},
- {"arm", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"arm", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"arm64", "internal/runtime/atomic", "And"}: struct{}{},
{"arm64", "internal/runtime/atomic", "And32"}: struct{}{},
{"arm64", "internal/runtime/atomic", "And64"}: struct{}{},
@@ -364,8 +364,8 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"arm64", "sync/atomic", "SwapUint32"}: struct{}{},
{"arm64", "sync/atomic", "SwapUint64"}: struct{}{},
{"arm64", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"arm64", "crypto/subtle", "ConstantTimeSelect"}: struct{}{},
- {"arm64", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"arm64", "crypto/internal/constanttime", "Select"}: struct{}{},
+ {"arm64", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"loong64", "internal/runtime/atomic", "And"}: struct{}{},
{"loong64", "internal/runtime/atomic", "And32"}: struct{}{},
{"loong64", "internal/runtime/atomic", "And64"}: struct{}{},
@@ -512,8 +512,8 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"loong64", "sync/atomic", "SwapUint32"}: struct{}{},
{"loong64", "sync/atomic", "SwapUint64"}: struct{}{},
{"loong64", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"loong64", "crypto/subtle", "ConstantTimeSelect"}: struct{}{},
- {"loong64", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"loong64", "crypto/internal/constanttime", "Select"}: struct{}{},
+ {"loong64", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"mips", "internal/runtime/atomic", "And"}: struct{}{},
{"mips", "internal/runtime/atomic", "And8"}: struct{}{},
{"mips", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -585,7 +585,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mips", "sync/atomic", "SwapInt32"}: struct{}{},
{"mips", "sync/atomic", "SwapUint32"}: struct{}{},
{"mips", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"mips", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"mips", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"mips64", "internal/runtime/atomic", "And"}: struct{}{},
{"mips64", "internal/runtime/atomic", "And8"}: struct{}{},
{"mips64", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -674,7 +674,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mips64", "sync/atomic", "SwapUint32"}: struct{}{},
{"mips64", "sync/atomic", "SwapUint64"}: struct{}{},
{"mips64", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"mips64", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"mips64", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"mips64le", "internal/runtime/atomic", "And"}: struct{}{},
{"mips64le", "internal/runtime/atomic", "And8"}: struct{}{},
{"mips64le", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -763,7 +763,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mips64le", "sync/atomic", "SwapUint32"}: struct{}{},
{"mips64le", "sync/atomic", "SwapUint64"}: struct{}{},
{"mips64le", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"mips64le", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"mips64le", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"mipsle", "internal/runtime/atomic", "And"}: struct{}{},
{"mipsle", "internal/runtime/atomic", "And8"}: struct{}{},
{"mipsle", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -835,7 +835,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"mipsle", "sync/atomic", "SwapInt32"}: struct{}{},
{"mipsle", "sync/atomic", "SwapUint32"}: struct{}{},
{"mipsle", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"mipsle", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"mipsle", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"ppc64", "internal/runtime/atomic", "And"}: struct{}{},
{"ppc64", "internal/runtime/atomic", "And8"}: struct{}{},
{"ppc64", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -960,8 +960,8 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"ppc64", "sync/atomic", "SwapUint32"}: struct{}{},
{"ppc64", "sync/atomic", "SwapUint64"}: struct{}{},
{"ppc64", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"ppc64", "crypto/subtle", "ConstantTimeSelect"}: struct{}{},
- {"ppc64", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"ppc64", "crypto/internal/constanttime", "Select"}: struct{}{},
+ {"ppc64", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"ppc64le", "internal/runtime/atomic", "And"}: struct{}{},
{"ppc64le", "internal/runtime/atomic", "And8"}: struct{}{},
{"ppc64le", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -1086,8 +1086,8 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"ppc64le", "sync/atomic", "SwapUint32"}: struct{}{},
{"ppc64le", "sync/atomic", "SwapUint64"}: struct{}{},
{"ppc64le", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"ppc64le", "crypto/subtle", "ConstantTimeSelect"}: struct{}{},
- {"ppc64le", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"ppc64le", "crypto/internal/constanttime", "Select"}: struct{}{},
+ {"ppc64le", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"riscv64", "internal/runtime/atomic", "And"}: struct{}{},
{"riscv64", "internal/runtime/atomic", "And8"}: struct{}{},
{"riscv64", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -1208,7 +1208,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"riscv64", "sync/atomic", "SwapUint32"}: struct{}{},
{"riscv64", "sync/atomic", "SwapUint64"}: struct{}{},
{"riscv64", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"riscv64", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"riscv64", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"s390x", "internal/runtime/atomic", "And"}: struct{}{},
{"s390x", "internal/runtime/atomic", "And8"}: struct{}{},
{"s390x", "internal/runtime/atomic", "Cas"}: struct{}{},
@@ -1327,7 +1327,7 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"s390x", "sync/atomic", "SwapUint32"}: struct{}{},
{"s390x", "sync/atomic", "SwapUint64"}: struct{}{},
{"s390x", "sync/atomic", "SwapUintptr"}: struct{}{},
- {"s390x", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"s390x", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
{"wasm", "internal/runtime/sys", "GetCallerPC"}: struct{}{},
{"wasm", "internal/runtime/sys", "GetCallerSP"}: struct{}{},
{"wasm", "internal/runtime/sys", "GetClosurePtr"}: struct{}{},
@@ -1363,8 +1363,8 @@ var wantIntrinsics = map[testIntrinsicKey]struct{}{
{"wasm", "math/bits", "TrailingZeros8"}: struct{}{},
{"wasm", "runtime", "KeepAlive"}: struct{}{},
{"wasm", "runtime", "slicebytetostringtmp"}: struct{}{},
- {"wasm", "crypto/subtle", "ConstantTimeSelect"}: struct{}{},
- {"wasm", "crypto/subtle", "constantTimeBoolToUint8"}: struct{}{},
+ {"wasm", "crypto/internal/constanttime", "Select"}: struct{}{},
+ {"wasm", "crypto/internal/constanttime", "boolToUint8"}: struct{}{},
}
func TestIntrinsics(t *testing.T) {
diff --git a/src/crypto/internal/constanttime/constant_time.go b/src/crypto/internal/constanttime/constant_time.go
new file mode 100644
index 0000000000..5525307195
--- /dev/null
+++ b/src/crypto/internal/constanttime/constant_time.go
@@ -0,0 +1,42 @@
+// Copyright 2025 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 constanttime
+
+// The functions in this package are compiler intrinsics for constant-time
+// operations. They are exposed by crypto/subtle and used directly by the
+// FIPS 140-3 module.
+
+// Select returns x if v == 1 and y if v == 0.
+// Its behavior is undefined if v takes any other value.
+func Select(v, x, y int) int {
+ // This is intrinsicified on arches with CMOV.
+ // It implements the following superset behavior:
+ // ConstantTimeSelect returns x if v != 0 and y if v == 0.
+ // Do the same here to avoid non portable UB.
+ v = int(boolToUint8(v != 0))
+ return ^(v-1)&x | (v-1)&y
+}
+
+// ByteEq returns 1 if x == y and 0 otherwise.
+func ByteEq(x, y uint8) int {
+ return int(boolToUint8(x == y))
+}
+
+// Eq returns 1 if x == y and 0 otherwise.
+func Eq(x, y int32) int {
+ return int(boolToUint8(x == y))
+}
+
+// LessOrEq returns 1 if x <= y and 0 otherwise.
+// Its behavior is undefined if x or y are negative or > 2**31 - 1.
+func LessOrEq(x, y int) int {
+ return int(boolToUint8(x <= y))
+}
+
+// boolToUint8 is a compiler intrinsic.
+// It returns 1 for true and 0 for false.
+func boolToUint8(b bool) uint8 {
+ panic("unreachable; must be intrinsicified")
+}
diff --git a/src/crypto/internal/fips140/edwards25519/tables.go b/src/crypto/internal/fips140/edwards25519/tables.go
index 801b76771d..7da3f7b15b 100644
--- a/src/crypto/internal/fips140/edwards25519/tables.go
+++ b/src/crypto/internal/fips140/edwards25519/tables.go
@@ -4,9 +4,7 @@
package edwards25519
-import (
- "crypto/internal/fips140/subtle"
-)
+import "crypto/internal/constanttime"
// A dynamic lookup table for variable-base, constant-time scalar muls.
type projLookupTable struct {
@@ -95,7 +93,7 @@ func (v *projLookupTable) SelectInto(dest *projCached, x int8) {
dest.Zero()
for j := 1; j <= 8; j++ {
// Set dest = j*Q if |x| = j
- cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
+ cond := constanttime.ByteEq(xabs, uint8(j))
dest.Select(&v.points[j-1], dest, cond)
}
// Now dest = |x|*Q, conditionally negate to get x*Q
@@ -111,7 +109,7 @@ func (v *affineLookupTable) SelectInto(dest *affineCached, x int8) {
dest.Zero()
for j := 1; j <= 8; j++ {
// Set dest = j*Q if |x| = j
- cond := subtle.ConstantTimeByteEq(xabs, uint8(j))
+ cond := constanttime.ByteEq(xabs, uint8(j))
dest.Select(&v.points[j-1], dest, cond)
}
// Now dest = |x|*Q, conditionally negate to get x*Q
diff --git a/src/crypto/internal/fips140/nistec/generate.go b/src/crypto/internal/fips140/nistec/generate.go
index 7786dc556f..75b1ac60f0 100644
--- a/src/crypto/internal/fips140/nistec/generate.go
+++ b/src/crypto/internal/fips140/nistec/generate.go
@@ -140,8 +140,8 @@ const tmplNISTEC = `// Copyright 2022 The Go Authors. All rights reserved.
package nistec
import (
+ "crypto/internal/constanttime"
"crypto/internal/fips140/nistec/fiat"
- "crypto/internal/fips140/subtle"
"errors"
"sync"
)
@@ -467,7 +467,7 @@ func (table *{{.p}}Table) Select(p *{{.P}}Point, n uint8) {
}
p.Set(New{{.P}}Point())
for i := uint8(1); i < 16; i++ {
- cond := subtle.ConstantTimeByteEq(i, n)
+ cond := constanttime.ByteEq(i, n)
p.Select(table[i-1], p, cond)
}
}
diff --git a/src/crypto/internal/fips140/nistec/p224.go b/src/crypto/internal/fips140/nistec/p224.go
index 82bced251f..7965b18689 100644
--- a/src/crypto/internal/fips140/nistec/p224.go
+++ b/src/crypto/internal/fips140/nistec/p224.go
@@ -7,8 +7,8 @@
package nistec
import (
+ "crypto/internal/constanttime"
"crypto/internal/fips140/nistec/fiat"
- "crypto/internal/fips140/subtle"
"errors"
"sync"
)
@@ -333,7 +333,7 @@ func (table *p224Table) Select(p *P224Point, n uint8) {
}
p.Set(NewP224Point())
for i := uint8(1); i < 16; i++ {
- cond := subtle.ConstantTimeByteEq(i, n)
+ cond := constanttime.ByteEq(i, n)
p.Select(table[i-1], p, cond)
}
}
diff --git a/src/crypto/internal/fips140/nistec/p256.go b/src/crypto/internal/fips140/nistec/p256.go
index c957c54247..650bde4e73 100644
--- a/src/crypto/internal/fips140/nistec/p256.go
+++ b/src/crypto/internal/fips140/nistec/p256.go
@@ -7,8 +7,8 @@
package nistec
import (
+ "crypto/internal/constanttime"
"crypto/internal/fips140/nistec/fiat"
- "crypto/internal/fips140/subtle"
"crypto/internal/fips140deps/byteorder"
"crypto/internal/fips140deps/cpu"
"errors"
@@ -458,7 +458,7 @@ func (table *p256Table) Select(p *P256Point, n uint8) {
}
p.Set(NewP256Point())
for i := uint8(1); i <= 16; i++ {
- cond := subtle.ConstantTimeByteEq(i, n)
+ cond := constanttime.ByteEq(i, n)
p.Select(&table[i-1], p, cond)
}
}
@@ -553,7 +553,7 @@ func (table *p256AffineTable) Select(p *p256AffinePoint, n uint8) {
panic("nistec: internal error: p256AffineTable.Select called with out-of-bounds value")
}
for i := uint8(1); i <= 32; i++ {
- cond := subtle.ConstantTimeByteEq(i, n)
+ cond := constanttime.ByteEq(i, n)
p.x.Select(&table[i-1].x, &p.x, cond)
p.y.Select(&table[i-1].y, &p.y, cond)
}
@@ -618,7 +618,7 @@ func (p *P256Point) ScalarBaseMult(scalar []byte) (*P256Point, error) {
// the point at infinity (because infinity can't be represented in affine
// coordinates). Here we conditionally set p to the infinity if sel is zero.
// In the loop, that's handled by AddAffine.
- selIsZero := subtle.ConstantTimeByteEq(sel, 0)
+ selIsZero := constanttime.ByteEq(sel, 0)
p.Select(NewP256Point(), t.Projective(), selIsZero)
for index >= 5 {
@@ -636,7 +636,7 @@ func (p *P256Point) ScalarBaseMult(scalar []byte) (*P256Point, error) {
table := &p256GeneratorTables[(index+1)/6]
table.Select(t, sel)
t.Negate(sign)
- selIsZero := subtle.ConstantTimeByteEq(sel, 0)
+ selIsZero := constanttime.ByteEq(sel, 0)
p.AddAffine(p, t, selIsZero)
}
diff --git a/src/crypto/internal/fips140/nistec/p384.go b/src/crypto/internal/fips140/nistec/p384.go
index 318c08a979..352f1a806e 100644
--- a/src/crypto/internal/fips140/nistec/p384.go
+++ b/src/crypto/internal/fips140/nistec/p384.go
@@ -7,8 +7,8 @@
package nistec
import (
+ "crypto/internal/constanttime"
"crypto/internal/fips140/nistec/fiat"
- "crypto/internal/fips140/subtle"
"errors"
"sync"
)
@@ -333,7 +333,7 @@ func (table *p384Table) Select(p *P384Point, n uint8) {
}
p.Set(NewP384Point())
for i := uint8(1); i < 16; i++ {
- cond := subtle.ConstantTimeByteEq(i, n)
+ cond := constanttime.ByteEq(i, n)
p.Select(table[i-1], p, cond)
}
}
diff --git a/src/crypto/internal/fips140/nistec/p521.go b/src/crypto/internal/fips140/nistec/p521.go
index 8ade8a3304..429f637993 100644
--- a/src/crypto/internal/fips140/nistec/p521.go
+++ b/src/crypto/internal/fips140/nistec/p521.go
@@ -7,8 +7,8 @@
package nistec
import (
+ "crypto/internal/constanttime"
"crypto/internal/fips140/nistec/fiat"
- "crypto/internal/fips140/subtle"
"errors"
"sync"
)
@@ -333,7 +333,7 @@ func (table *p521Table) Select(p *P521Point, n uint8) {
}
p.Set(NewP521Point())
for i := uint8(1); i < 16; i++ {
- cond := subtle.ConstantTimeByteEq(i, n)
+ cond := constanttime.ByteEq(i, n)
p.Select(table[i-1], p, cond)
}
}
diff --git a/src/crypto/internal/fips140/rsa/pkcs1v22.go b/src/crypto/internal/fips140/rsa/pkcs1v22.go
index 94e7345996..29c47069a3 100644
--- a/src/crypto/internal/fips140/rsa/pkcs1v22.go
+++ b/src/crypto/internal/fips140/rsa/pkcs1v22.go
@@ -9,6 +9,7 @@ package rsa
import (
"bytes"
+ "crypto/internal/constanttime"
"crypto/internal/fips140"
"crypto/internal/fips140/drbg"
"crypto/internal/fips140/sha256"
@@ -432,7 +433,7 @@ func DecryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l
hash.Write(label)
lHash := hash.Sum(nil)
- firstByteIsZero := subtle.ConstantTimeByteEq(em[0], 0)
+ firstByteIsZero := constanttime.ByteEq(em[0], 0)
seed := em[1 : hash.Size()+1]
db := em[hash.Size()+1:]
@@ -458,11 +459,11 @@ func DecryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l
rest := db[hash.Size():]
for i := 0; i < len(rest); i++ {
- equals0 := subtle.ConstantTimeByteEq(rest[i], 0)
- equals1 := subtle.ConstantTimeByteEq(rest[i], 1)
- index = subtle.ConstantTimeSelect(lookingForIndex&equals1, i, index)
- lookingForIndex = subtle.ConstantTimeSelect(equals1, 0, lookingForIndex)
- invalid = subtle.ConstantTimeSelect(lookingForIndex&^equals0, 1, invalid)
+ equals0 := constanttime.ByteEq(rest[i], 0)
+ equals1 := constanttime.ByteEq(rest[i], 1)
+ index = constanttime.Select(lookingForIndex&equals1, i, index)
+ lookingForIndex = constanttime.Select(equals1, 0, lookingForIndex)
+ invalid = constanttime.Select(lookingForIndex&^equals0, 1, invalid)
}
if firstByteIsZero&lHash2Good&^invalid&^lookingForIndex != 1 {
diff --git a/src/crypto/internal/fips140/subtle/constant_time.go b/src/crypto/internal/fips140/subtle/constant_time.go
index fa7a002d3f..fc1e307985 100644
--- a/src/crypto/internal/fips140/subtle/constant_time.go
+++ b/src/crypto/internal/fips140/subtle/constant_time.go
@@ -5,6 +5,7 @@
package subtle
import (
+ "crypto/internal/constanttime"
"crypto/internal/fips140deps/byteorder"
"math/bits"
)
@@ -24,7 +25,7 @@ func ConstantTimeCompare(x, y []byte) int {
v |= x[i] ^ y[i]
}
- return ConstantTimeByteEq(v, 0)
+ return constanttime.ByteEq(v, 0)
}
// ConstantTimeLessOrEqBytes returns 1 if x <= y and 0 otherwise. The comparison
@@ -58,20 +59,6 @@ func ConstantTimeLessOrEqBytes(x, y []byte) int {
return int(b ^ 1)
}
-// ConstantTimeSelect returns x if v == 1 and y if v == 0.
-// Its behavior is undefined if v takes any other value.
-func ConstantTimeSelect(v, x, y int) int { return ^(v-1)&x | (v-1)&y }
-
-// ConstantTimeByteEq returns 1 if x == y and 0 otherwise.
-func ConstantTimeByteEq(x, y uint8) int {
- return int((uint32(x^y) - 1) >> 31)
-}
-
-// ConstantTimeEq returns 1 if x == y and 0 otherwise.
-func ConstantTimeEq(x, y int32) int {
- return int((uint64(uint32(x^y)) - 1) >> 63)
-}
-
// ConstantTimeCopy copies the contents of y into x (a slice of equal length)
// if v == 1. If v == 0, x is left unchanged. Its behavior is undefined if v
// takes any other value.
@@ -86,11 +73,3 @@ func ConstantTimeCopy(v int, x, y []byte) {
x[i] = x[i]&xmask | y[i]&ymask
}
}
-
-// ConstantTimeLessOrEq returns 1 if x <= y and 0 otherwise.
-// Its behavior is undefined if x or y are negative or > 2**31 - 1.
-func ConstantTimeLessOrEq(x, y int) int {
- x32 := int32(x)
- y32 := int32(y)
- return int(((x32 - y32 - 1) >> 31) & 1)
-}
diff --git a/src/crypto/internal/fips140deps/fipsdeps_test.go b/src/crypto/internal/fips140deps/fipsdeps_test.go
index 3eaae1830d..29a56047c3 100644
--- a/src/crypto/internal/fips140deps/fipsdeps_test.go
+++ b/src/crypto/internal/fips140deps/fipsdeps_test.go
@@ -28,6 +28,9 @@ var AllowedInternalPackages = map[string]bool{
// randutil.MaybeReadByte is used in non-FIPS mode by GenerateKey functions.
"crypto/internal/randutil": true,
+
+ // constanttime are the constant-time intrinsics.
+ "crypto/internal/constanttime": true,
}
func TestImports(t *testing.T) {
diff --git a/src/crypto/subtle/constant_time.go b/src/crypto/subtle/constant_time.go
index 8eeff3b629..14c911101b 100644
--- a/src/crypto/subtle/constant_time.go
+++ b/src/crypto/subtle/constant_time.go
@@ -6,63 +6,47 @@
// code but require careful thought to use correctly.
package subtle
-import "crypto/internal/fips140/subtle"
+import (
+ "crypto/internal/constanttime"
+ "crypto/internal/fips140/subtle"
+)
+
+// These functions are forwarded to crypto/internal/constanttime for intrinsified
+// operations, and to crypto/internal/fips140/subtle for byte slice operations.
// ConstantTimeCompare returns 1 if the two slices, x and y, have equal contents
// and 0 otherwise. The time taken is a function of the length of the slices and
// is independent of the contents. If the lengths of x and y do not match it
// returns 0 immediately.
func ConstantTimeCompare(x, y []byte) int {
- if len(x) != len(y) {
- return 0
- }
-
- var v byte
-
- for i := 0; i < len(x); i++ {
- v |= x[i] ^ y[i]
- }
-
- return ConstantTimeByteEq(v, 0)
+ return subtle.ConstantTimeCompare(x, y)
}
// ConstantTimeSelect returns x if v == 1 and y if v == 0.
// Its behavior is undefined if v takes any other value.
func ConstantTimeSelect(v, x, y int) int {
- // This is intrinsicified on arches with CMOV.
- // It implements the following superset behavior:
- // ConstantTimeSelect returns x if v != 0 and y if v == 0.
- // Do the same here to avoid non portable UB.
- v = int(constantTimeBoolToUint8(v != 0))
- return ^(v-1)&x | (v-1)&y
+ return constanttime.Select(v, x, y)
}
// ConstantTimeByteEq returns 1 if x == y and 0 otherwise.
func ConstantTimeByteEq(x, y uint8) int {
- return int(constantTimeBoolToUint8(x == y))
+ return constanttime.ByteEq(x, y)
}
// ConstantTimeEq returns 1 if x == y and 0 otherwise.
func ConstantTimeEq(x, y int32) int {
- return int(constantTimeBoolToUint8(x == y))
+ return constanttime.Eq(x, y)
}
// ConstantTimeCopy copies the contents of y into x (a slice of equal length)
// if v == 1. If v == 0, x is left unchanged. Its behavior is undefined if v
// takes any other value.
func ConstantTimeCopy(v int, x, y []byte) {
- // Forward this one since it gains nothing from compiler intrinsics.
subtle.ConstantTimeCopy(v, x, y)
}
// ConstantTimeLessOrEq returns 1 if x <= y and 0 otherwise.
// Its behavior is undefined if x or y are negative or > 2**31 - 1.
func ConstantTimeLessOrEq(x, y int) int {
- return int(constantTimeBoolToUint8(x <= y))
-}
-
-// constantTimeBoolToUint8 is a compiler intrinsic.
-// It returns 1 for true and 0 for false.
-func constantTimeBoolToUint8(b bool) uint8 {
- panic("unreachable; must be intrinsicified")
+ return constanttime.LessOrEq(x, y)
}
diff --git a/src/go/build/deps_test.go b/src/go/build/deps_test.go
index bc7eae69de..48a9f3e75b 100644
--- a/src/go/build/deps_test.go
+++ b/src/go/build/deps_test.go
@@ -479,6 +479,8 @@ var depsRules = `
io, math/rand/v2 < crypto/internal/randutil;
+ NONE < crypto/internal/constanttime;
+
STR < crypto/internal/impl;
OS < crypto/internal/sysrand
@@ -496,6 +498,7 @@ var depsRules = `
crypto/internal/impl,
crypto/internal/entropy,
crypto/internal/randutil,
+ crypto/internal/constanttime,
crypto/internal/entropy/v1.0.0,
crypto/internal/fips140deps/byteorder,
crypto/internal/fips140deps/cpu,