]> Cypherpunks repositories - gostls13.git/commitdiff
crypto/internal/constanttime: expose intrinsics to the FIPS 140-3 packages
authorFilippo Valsorda <filippo@golang.org>
Wed, 29 Oct 2025 12:05:19 +0000 (13:05 +0100)
committerGopher Robot <gobot@golang.org>
Mon, 3 Nov 2025 15:14:16 +0000 (07:14 -0800)
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>
14 files changed:
src/cmd/compile/internal/ssagen/intrinsics.go
src/cmd/compile/internal/ssagen/intrinsics_test.go
src/crypto/internal/constanttime/constant_time.go [new file with mode: 0644]
src/crypto/internal/fips140/edwards25519/tables.go
src/crypto/internal/fips140/nistec/generate.go
src/crypto/internal/fips140/nistec/p224.go
src/crypto/internal/fips140/nistec/p256.go
src/crypto/internal/fips140/nistec/p384.go
src/crypto/internal/fips140/nistec/p521.go
src/crypto/internal/fips140/rsa/pkcs1v22.go
src/crypto/internal/fips140/subtle/constant_time.go
src/crypto/internal/fips140deps/fipsdeps_test.go
src/crypto/subtle/constant_time.go
src/go/build/deps_test.go

index bf9e71c1701d0816259d0fc192f7006cfa6e98be..190c4840ce9aad42dde485479f65ea0de80d8eb0 100644 (file)
@@ -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])
                },
index 9311f843454c362c14b49c4998ef1f99f97ffb9b..782426215c9fff33f4a47fb8025398555343b534 100644 (file)
@@ -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 (file)
index 0000000..5525307
--- /dev/null
@@ -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")
+}
index 801b76771d1ea3e6a32756aaabde3ff5ff7d73f2..7da3f7b15bca634e64966a1e4ffdf8788411b777 100644 (file)
@@ -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
index 7786dc556f52600ce6ff8f84d1e3d670b703a0b7..75b1ac60f0b6ecf9db2c780a96901589e151d2c4 100644 (file)
@@ -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)
        }
 }
index 82bced251fe0ac72710f94c7276c3eb38d842ca9..7965b186891b0b28c85833888ecab9692f87160d 100644 (file)
@@ -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)
        }
 }
index c957c5424737b01e8f6e6235a66c0b3dfd6f8abe..650bde4e73e0a7793092fc3b84d63909b980a5d4 100644 (file)
@@ -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)
        }
 
index 318c08a97972f753c5cee62581ba13d72fc7e23c..352f1a806e8ee48e71e1b7d1d3dd5e6dcf9100bc 100644 (file)
@@ -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)
        }
 }
index 8ade8a33040b7a377f48e09d5587476295e150c2..429f6379934904eb9b2572c8ba6cb849e3da4e60 100644 (file)
@@ -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)
        }
 }
index 94e7345996a46fb0302d3527ee735cc99d9b15d4..29c47069a3e0ee0f35cfde316c0eeb674ed787e2 100644 (file)
@@ -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 {
index fa7a002d3fa45642d5042ae86fa6f3c9c5e30b4b..fc1e3079855e94cbbb2093968f8bb0dcc9429ff1 100644 (file)
@@ -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)
-}
index 3eaae1830d0e18d4b5f8280a97f98d34d861ec4e..29a56047c3c5fd3f36f48a7d9d059027cdb9e672 100644 (file)
@@ -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) {
index 8eeff3b629befb9ee91508de8d4849e2a28af31b..14c911101b0fb81e39738999b9157bcfa00aa5e4 100644 (file)
@@ -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)
 }
index bc7eae69def15f894528b82f35857a88652ed994..48a9f3e75bb7b5b5bc028f0e8524024264d72f7f 100644 (file)
@@ -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,