diff --git a/ct/x509/x509.go b/ct/x509/x509.go index d6a8ee30..ebc27bfc 100644 --- a/ct/x509/x509.go +++ b/ct/x509/x509.go @@ -781,8 +781,10 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ } pub := &rsa.PublicKey{ - E: p.E, - N: p.N, + // Defensively copy E so callers cannot mutate it through the + // parsed wire structure. + E: new(big.Int).Set(p.E), + N: new(big.Int).Set(p.N), } return pub, nil case DSA: diff --git a/internal/testenv/testenv.go b/internal/testenv/testenv.go index d6a2e8b0..7fe48331 100644 --- a/internal/testenv/testenv.go +++ b/internal/testenv/testenv.go @@ -33,7 +33,7 @@ func Builder() string { return os.Getenv("GO_BUILDER_NAME") } -// HasGoBuild reports whether the current system can build programs with ``go build'' +// HasGoBuild reports whether the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. func HasGoBuild() bool { if os.Getenv("GO_GCFLAGS") != "" { @@ -50,7 +50,7 @@ func HasGoBuild() bool { return true } -// MustHaveGoBuild checks that the current system can build programs with ``go build'' +// MustHaveGoBuild checks that the current system can build programs with “go build” // and then run them with os.StartProcess or exec.Command. // If not, MustHaveGoBuild calls t.Skip with an explanation. func MustHaveGoBuild(t testing.TB) { @@ -62,13 +62,13 @@ func MustHaveGoBuild(t testing.TB) { } } -// HasGoRun reports whether the current system can run programs with ``go run.'' +// HasGoRun reports whether the current system can run programs with “go run.” func HasGoRun() bool { // For now, having go run and having go build are the same. return HasGoBuild() } -// MustHaveGoRun checks that the current system can run programs with ``go run.'' +// MustHaveGoRun checks that the current system can run programs with “go run.” // If not, MustHaveGoRun calls t.Skip with an explanation. func MustHaveGoRun(t testing.TB) { if !HasGoRun() { diff --git a/rsa/rsa_test.go b/rsa/rsa_test.go index d7775f24..48298ef7 100644 --- a/rsa/rsa_test.go +++ b/rsa/rsa_test.go @@ -895,3 +895,32 @@ var testEncryptOAEPData = []testEncryptOAEPStruct{ }, }, } + +// TestPublicKeyDefensiveCopy verifies that mutating the caller's PublicKey +// after performing an operation does not affect subsequent operations on the +// same key via the precomputed FIPS state. +func TestPublicKeyDefensiveCopy(t *testing.T) { + priv, err := GenerateKey(rand.Reader, 2048) + if err != nil { + t.Fatal(err) + } + msg := []byte("hello") + ct, err := EncryptPKCS1v15(rand.Reader, &priv.PublicKey, msg) + if err != nil { + t.Fatal(err) + } + pt, err := DecryptPKCS1v15(rand.Reader, priv, ct) + if err != nil || !bytes.Equal(pt, msg) { + t.Fatalf("decrypt mismatch: %v / %x", err, pt) + } + // Mutate the caller's E in place. A correctly defensively-copied internal + // state must continue to function. + originalE := new(big.Int).Set(priv.PublicKey.E) + priv.PublicKey.E.SetInt64(7) + pt2, err := DecryptPKCS1v15(rand.Reader, priv, ct) + // Restore E so subsequent tests aren't affected. + priv.PublicKey.E.Set(originalE) + if err != nil || !bytes.Equal(pt2, msg) { + t.Fatalf("decrypt after caller-side E mutation failed (defensive copy missing?): %v / %x", err, pt2) + } +} diff --git a/x509/pkcs1.go b/x509/pkcs1.go index dc5fb448..f688930f 100644 --- a/x509/pkcs1.go +++ b/x509/pkcs1.go @@ -117,8 +117,10 @@ func ParsePKCS1PublicKey(der []byte) (*rsa.PublicKey, error) { //} return &rsa.PublicKey{ - E: pub.E, - N: pub.N, + // Defensively copy E so callers cannot mutate it through the + // parsed wire structure. + E: new(big.Int).Set(pub.E), + N: new(big.Int).Set(pub.N), }, nil } diff --git a/x509/x509.go b/x509/x509.go index bc64042e..2c41e49d 100644 --- a/x509/x509.go +++ b/x509/x509.go @@ -1346,8 +1346,10 @@ func parsePublicKey(algo PublicKeyAlgorithm, keyData *publicKeyInfo) (interface{ } return &rsa.PublicKey{ - E: p.E, - N: p.N, + // Defensively copy E so callers cannot mutate it through the + // parsed wire structure. + E: new(big.Int).Set(p.E), + N: new(big.Int).Set(p.N), }, nil case DSA: var p *big.Int