Skip to content

Commit 9b7a328

Browse files
committed
crypto/internal/fips140: remove key import PCTs, make keygen PCTs fatal
CMVP clarified with the September 2nd changes to IG 10.3.A that PCTs don't need to run on imported keys. However, PCT failure must enter the error state (which for us is fatal). Thankfully, now that PCTs only run on key generation, we can be assured they will never fail. This change should only affect FIPS 140-3 mode. While at it, make the CAST/PCT testing more robust, checking TestConditional is terminated by a fatal error (and not by t.Fatal). Fixes #74947 Updates #69536 Change-Id: I6a6a696439e1560c10f3cce2cb208fd40c5bc641 Reviewed-on: https://go-review.googlesource.com/c/go/+/701517 Reviewed-by: Daniel McCarney <[email protected]> Reviewed-by: Junyang Shao <[email protected]> Reviewed-by: Roland Shoemaker <[email protected]> TryBot-Bypass: Filippo Valsorda <[email protected]>
1 parent 7f9ab72 commit 9b7a328

File tree

11 files changed

+107
-135
lines changed

11 files changed

+107
-135
lines changed

src/crypto/internal/fips140/cast.go

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,27 +56,34 @@ func CAST(name string, f func() error) {
5656
}
5757

5858
// PCT runs the named Pairwise Consistency Test (if operated in FIPS mode) and
59-
// returns any errors. If an error is returned, the key must not be used.
59+
// aborts the program (stopping the module input/output and entering the "error
60+
// state") if the test fails.
6061
//
61-
// PCTs are mandatory for every key pair that is generated/imported, including
62+
// PCTs are mandatory for every generated (but not imported) key pair, including
6263
// ephemeral keys (which effectively doubles the cost of key establishment). See
6364
// Implementation Guidance 10.3.A Additional Comment 1.
6465
//
6566
// The name must not contain commas, colons, hashes, or equal signs.
6667
//
6768
// If a package p calls PCT during key generation, an invocation of that
6869
// function should be added to fipstest.TestConditionals.
69-
func PCT(name string, f func() error) error {
70+
func PCT(name string, f func() error) {
7071
if strings.ContainsAny(name, ",#=:") {
7172
panic("fips: invalid self-test name: " + name)
7273
}
7374
if !Enabled {
74-
return nil
75+
return
7576
}
7677

7778
err := f()
7879
if name == failfipscast {
7980
err = errors.New("simulated PCT failure")
8081
}
81-
return err
82+
if err != nil {
83+
fatal("FIPS 140-3 self-test failed: " + name + ": " + err.Error())
84+
panic("unreachable")
85+
}
86+
if debug {
87+
println("FIPS 140-3 PCT passed:", name)
88+
}
8289
}

src/crypto/internal/fips140/ecdh/ecdh.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,27 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
161161
if err != nil {
162162
continue
163163
}
164+
165+
// A "Pairwise Consistency Test" makes no sense if we just generated the
166+
// public key from an ephemeral private key. Moreover, there is no way to
167+
// check it aside from redoing the exact same computation again. SP 800-56A
168+
// Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it.
169+
// However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a
170+
// PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional
171+
// Comment 1 goes out of its way to say that "the PCT shall be performed
172+
// consistent [...], even if the underlying standard does not require a
173+
// PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode.
174+
fips140.PCT("ECDH PCT", func() error {
175+
p1, err := c.newPoint().ScalarBaseMult(privateKey.d)
176+
if err != nil {
177+
return err
178+
}
179+
if !bytes.Equal(p1.Bytes(), privateKey.pub.q) {
180+
return errors.New("crypto/ecdh: public key does not match private key")
181+
}
182+
return nil
183+
})
184+
164185
return privateKey, nil
165186
}
166187
}
@@ -188,28 +209,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], key []byte) (*PrivateKey, error) {
188209
panic("crypto/ecdh: internal error: public key is the identity element")
189210
}
190211

191-
// A "Pairwise Consistency Test" makes no sense if we just generated the
192-
// public key from an ephemeral private key. Moreover, there is no way to
193-
// check it aside from redoing the exact same computation again. SP 800-56A
194-
// Rev. 3, Section 5.6.2.1.4 acknowledges that, and doesn't require it.
195-
// However, ISO 19790:2012, Section 7.10.3.3 has a blanket requirement for a
196-
// PCT for all generated keys (AS10.35) and FIPS 140-3 IG 10.3.A, Additional
197-
// Comment 1 goes out of its way to say that "the PCT shall be performed
198-
// consistent [...], even if the underlying standard does not require a
199-
// PCT". So we do it. And make ECDH nearly 50% slower (only) in FIPS mode.
200-
if err := fips140.PCT("ECDH PCT", func() error {
201-
p1, err := c.newPoint().ScalarBaseMult(key)
202-
if err != nil {
203-
return err
204-
}
205-
if !bytes.Equal(p1.Bytes(), publicKey) {
206-
return errors.New("crypto/ecdh: public key does not match private key")
207-
}
208-
return nil
209-
}); err != nil {
210-
panic(err)
211-
}
212-
213212
k := &PrivateKey{d: bytes.Clone(key), pub: PublicKey{curve: c.curve, q: publicKey}}
214213
return k, nil
215214
}

src/crypto/internal/fips140/ecdsa/cast.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ func testHash() []byte {
5151
}
5252
}
5353

54-
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) error {
55-
return fips140.PCT("ECDSA PCT", func() error {
54+
func fipsPCT[P Point[P]](c *Curve[P], k *PrivateKey) {
55+
fips140.PCT("ECDSA PCT", func() error {
5656
hash := testHash()
5757
drbg := newDRBG(sha512.New, k.d, bits2octets(P256(), hash), nil)
5858
sig, err := sign(c, k, drbg, hash)

src/crypto/internal/fips140/ecdsa/ecdsa.go

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -167,11 +167,6 @@ func NewPrivateKey[P Point[P]](c *Curve[P], D, Q []byte) (*PrivateKey, error) {
167167
return nil, err
168168
}
169169
priv := &PrivateKey{pub: *pub, d: d.Bytes(c.N)}
170-
if err := fipsPCT(c, priv); err != nil {
171-
// This can happen if the application went out of its way to make an
172-
// ecdsa.PrivateKey with a mismatching PublicKey.
173-
return nil, err
174-
}
175170
return priv, nil
176171
}
177172

@@ -204,10 +199,7 @@ func GenerateKey[P Point[P]](c *Curve[P], rand io.Reader) (*PrivateKey, error) {
204199
},
205200
d: k.Bytes(c.N),
206201
}
207-
if err := fipsPCT(c, priv); err != nil {
208-
// This clearly can't happen, but FIPS 140-3 mandates that we check it.
209-
panic(err)
210-
}
202+
fipsPCT(c, priv)
211203
return priv, nil
212204
}
213205

src/crypto/internal/fips140/ed25519/cast.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ import (
1212
"sync"
1313
)
1414

15-
func fipsPCT(k *PrivateKey) error {
16-
return fips140.PCT("Ed25519 sign and verify PCT", func() error {
15+
func fipsPCT(k *PrivateKey) {
16+
fips140.PCT("Ed25519 sign and verify PCT", func() error {
1717
return pairwiseTest(k)
1818
})
1919
}

src/crypto/internal/fips140/ed25519/ed25519.go

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,10 +69,7 @@ func generateKey(priv *PrivateKey) (*PrivateKey, error) {
6969
fips140.RecordApproved()
7070
drbg.Read(priv.seed[:])
7171
precomputePrivateKey(priv)
72-
if err := fipsPCT(priv); err != nil {
73-
// This clearly can't happen, but FIPS 140-3 requires that we check.
74-
panic(err)
75-
}
72+
fipsPCT(priv)
7673
return priv, nil
7774
}
7875

@@ -88,10 +85,6 @@ func newPrivateKeyFromSeed(priv *PrivateKey, seed []byte) (*PrivateKey, error) {
8885
}
8986
copy(priv.seed[:], seed)
9087
precomputePrivateKey(priv)
91-
if err := fipsPCT(priv); err != nil {
92-
// This clearly can't happen, but FIPS 140-3 requires that we check.
93-
panic(err)
94-
}
9588
return priv, nil
9689
}
9790

@@ -137,12 +130,6 @@ func newPrivateKey(priv *PrivateKey, privBytes []byte) (*PrivateKey, error) {
137130

138131
copy(priv.prefix[:], h[32:])
139132

140-
if err := fipsPCT(priv); err != nil {
141-
// This can happen if the application messed with the private key
142-
// encoding, and the public key doesn't match the seed anymore.
143-
return nil, err
144-
}
145-
146133
return priv, nil
147134
}
148135

src/crypto/internal/fips140/mlkem/mlkem1024.go

Lines changed: 1 addition & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/crypto/internal/fips140/mlkem/mlkem768.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,10 +177,7 @@ func generateKey(dk *DecapsulationKey768) (*DecapsulationKey768, error) {
177177
var z [32]byte
178178
drbg.Read(z[:])
179179
kemKeyGen(dk, &d, &z)
180-
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil {
181-
// This clearly can't happen, but FIPS 140-3 requires us to check.
182-
panic(err)
183-
}
180+
fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) })
184181
fips140.RecordApproved()
185182
return dk, nil
186183
}
@@ -208,10 +205,6 @@ func newKeyFromSeed(dk *DecapsulationKey768, seed []byte) (*DecapsulationKey768,
208205
d := (*[32]byte)(seed[:32])
209206
z := (*[32]byte)(seed[32:])
210207
kemKeyGen(dk, d, z)
211-
if err := fips140.PCT("ML-KEM PCT", func() error { return kemPCT(dk) }); err != nil {
212-
// This clearly can't happen, but FIPS 140-3 requires us to check.
213-
panic(err)
214-
}
215208
fips140.RecordApproved()
216209
return dk, nil
217210
}

src/crypto/internal/fips140/rsa/keygen.go

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,28 @@ func GenerateKey(rand io.Reader, bits int) (*PrivateKey, error) {
105105
// negligible chance of failure we can defer the check to the end of key
106106
// generation and return an error if it fails. See [checkPrivateKey].
107107

108-
return newPrivateKey(N, 65537, d, P, Q)
108+
k, err := newPrivateKey(N, 65537, d, P, Q)
109+
if err != nil {
110+
return nil, err
111+
}
112+
113+
if k.fipsApproved {
114+
fips140.PCT("RSA sign and verify PCT", func() error {
115+
hash := []byte{
116+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
117+
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
118+
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
119+
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
120+
}
121+
sig, err := signPKCS1v15(k, "SHA-256", hash)
122+
if err != nil {
123+
return err
124+
}
125+
return verifyPKCS1v15(k.PublicKey(), "SHA-256", hash, sig)
126+
})
127+
}
128+
129+
return k, nil
109130
}
110131
}
111132

src/crypto/internal/fips140/rsa/rsa.go

Lines changed: 0 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -320,26 +320,6 @@ func checkPrivateKey(priv *PrivateKey) error {
320320
return errors.New("crypto/rsa: d too small")
321321
}
322322

323-
// If the key is still in scope for FIPS mode, perform a Pairwise
324-
// Consistency Test.
325-
if priv.fipsApproved {
326-
if err := fips140.PCT("RSA sign and verify PCT", func() error {
327-
hash := []byte{
328-
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
329-
0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10,
330-
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18,
331-
0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
332-
}
333-
sig, err := signPKCS1v15(priv, "SHA-256", hash)
334-
if err != nil {
335-
return err
336-
}
337-
return verifyPKCS1v15(priv.PublicKey(), "SHA-256", hash, sig)
338-
}); err != nil {
339-
return err
340-
}
341-
}
342-
343323
return nil
344324
}
345325

0 commit comments

Comments
 (0)