Skip to content

Commit

Permalink
crypto/rsa: refuse to generate and/or use keys smaller than 1024 bits
Browse files Browse the repository at this point in the history
Fixes #68762

Change-Id: Id89c770571d7cc27c6cf7932139ec3424383a7ef
Reviewed-on: https://go-review.googlesource.com/c/go/+/629938
Reviewed-by: Roland Shoemaker <[email protected]>
Auto-Submit: Filippo Valsorda <[email protected]>
Reviewed-by: Dmitri Shuralyov <[email protected]>
LUCI-TryBot-Result: Go LUCI <[email protected]>
  • Loading branch information
FiloSottile authored and gopherbot committed Nov 22, 2024
1 parent ca14eaf commit 10fb001
Show file tree
Hide file tree
Showing 16 changed files with 317 additions and 61 deletions.
4 changes: 4 additions & 0 deletions doc/godebug.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,10 @@ This behavior can be controlled with the `gotestjsonbuildtext` setting.
Using `gotestjsonbuildtext=1` restores the 1.23 behavior.
This setting will be removed in a future release, Go 1.28 at the earliest.

Go 1.24 changed [`crypto/rsa`](/pkg/crypto/rsa) to require RSA keys to be at
least 1024 bits. This behavior can be controlled with the `rsa1024min` setting.
Using `rsa1024min=0` restores the Go 1.23 behavior.

Go 1.24 introduced a mechanism for enabling platform specific Data Independent
Timing (DIT) modes in the [`crypto/subtle`](/pkg/crypto/subtle) package. This
mode can be enabled for an entire program with the `dataindependenttiming` setting.
Expand Down
7 changes: 7 additions & 0 deletions doc/next/6-stdlib/99-minor/crypto/rsa/68762.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[GenerateKey] now returns an error if a key of less than 1024 bits is requested.
All Sign, Verify, Encrypt, and Decrypt methods now return an error if used with
a key smaller than 1024 bits. Such keys are insecure and should not be used.
Setting `GODEBUG=rsa1024min=0` or including `//go:debug rsa1024min=0` in a
source file restores the old behavior, but we recommend doing so only in tests,
if necessary. A new [GenerateKey] example provides an easy-to-use standard
2048-bit test key.
2 changes: 2 additions & 0 deletions src/crypto/rsa/boring_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ import (
)

func TestBoringASN1Marshal(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")

k, err := GenerateKey(rand.Reader, 128)
if err != nil {
t.Fatal(err)
Expand Down
7 changes: 4 additions & 3 deletions src/crypto/rsa/equal_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,15 @@ package rsa_test

import (
"crypto"
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"testing"
)

func TestEqual(t *testing.T) {
private, _ := rsa.GenerateKey(rand.Reader, 512)
t.Setenv("GODEBUG", "rsa1024min=0")

private := test512Key
public := &private.PublicKey

if !public.Equal(public) {
Expand Down Expand Up @@ -41,7 +42,7 @@ func TestEqual(t *testing.T) {
t.Errorf("private key is not equal to itself after decoding: %v", private)
}

other, _ := rsa.GenerateKey(rand.Reader, 512)
other := test512KeyTwo
if public.Equal(other.Public()) {
t.Errorf("different public keys are Equal")
}
Expand Down
58 changes: 58 additions & 0 deletions src/crypto/rsa/example_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,69 @@ import (
"crypto/rand"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/hex"
"encoding/pem"
"fmt"
"os"
"strings"
)

func ExampleGenerateKey() {
privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
fmt.Fprintf(os.Stderr, "Error generating RSA key: %s", err)
return
}

der, err := x509.MarshalPKCS8PrivateKey(privateKey)
if err != nil {
fmt.Fprintf(os.Stderr, "Error marshalling RSA private key: %s", err)
return
}

fmt.Printf("%s", pem.EncodeToMemory(&pem.Block{
Type: "PRIVATE KEY",
Bytes: der,
}))
}

func ExampleGenerateKey_testKey() {
// This is an insecure, test-only key from RFC 9500, Section 2.1.
// It can be used in tests to avoid slow key generation.
block, _ := pem.Decode([]byte(strings.ReplaceAll(
`-----BEGIN RSA TESTING KEY-----
MIIEowIBAAKCAQEAsPnoGUOnrpiSqt4XynxA+HRP7S+BSObI6qJ7fQAVSPtRkqso
tWxQYLEYzNEx5ZSHTGypibVsJylvCfuToDTfMul8b/CZjP2Ob0LdpYrNH6l5hvFE
89FU1nZQF15oVLOpUgA7wGiHuEVawrGfey92UE68mOyUVXGweJIVDdxqdMoPvNNU
l86BU02vlBiESxOuox+dWmuVV7vfYZ79Toh/LUK43YvJh+rhv4nKuF7iHjVjBd9s
B6iDjj70HFldzOQ9r8SRI+9NirupPTkF5AKNe6kUhKJ1luB7S27ZkvB3tSTT3P59
3VVJvnzOjaA1z6Cz+4+eRvcysqhrRgFlwI9TEwIDAQABAoIBAEEYiyDP29vCzx/+
dS3LqnI5BjUuJhXUnc6AWX/PCgVAO+8A+gZRgvct7PtZb0sM6P9ZcLrweomlGezI
FrL0/6xQaa8bBr/ve/a8155OgcjFo6fZEw3Dz7ra5fbSiPmu4/b/kvrg+Br1l77J
aun6uUAs1f5B9wW+vbR7tzbT/mxaUeDiBzKpe15GwcvbJtdIVMa2YErtRjc1/5B2
BGVXyvlJv0SIlcIEMsHgnAFOp1ZgQ08aDzvilLq8XVMOahAhP1O2A3X8hKdXPyrx
IVWE9bS9ptTo+eF6eNl+d7htpKGEZHUxinoQpWEBTv+iOoHsVunkEJ3vjLP3lyI/
fY0NQ1ECgYEA3RBXAjgvIys2gfU3keImF8e/TprLge1I2vbWmV2j6rZCg5r/AS0u
pii5CvJ5/T5vfJPNgPBy8B/yRDs+6PJO1GmnlhOkG9JAIPkv0RBZvR0PMBtbp6nT
Y3yo1lwamBVBfY6rc0sLTzosZh2aGoLzrHNMQFMGaauORzBFpY5lU50CgYEAzPHl
u5DI6Xgep1vr8QvCUuEesCOgJg8Yh1UqVoY/SmQh6MYAv1I9bLGwrb3WW/7kqIoD
fj0aQV5buVZI2loMomtU9KY5SFIsPV+JuUpy7/+VE01ZQM5FdY8wiYCQiVZYju9X
Wz5LxMNoz+gT7pwlLCsC4N+R8aoBk404aF1gum8CgYAJ7VTq7Zj4TFV7Soa/T1eE
k9y8a+kdoYk3BASpCHJ29M5R2KEA7YV9wrBklHTz8VzSTFTbKHEQ5W5csAhoL5Fo
qoHzFFi3Qx7MHESQb9qHyolHEMNx6QdsHUn7rlEnaTTyrXh3ifQtD6C0yTmFXUIS
CW9wKApOrnyKJ9nI0HcuZQKBgQCMtoV6e9VGX4AEfpuHvAAnMYQFgeBiYTkBKltQ
XwozhH63uMMomUmtSG87Sz1TmrXadjAhy8gsG6I0pWaN7QgBuFnzQ/HOkwTm+qKw
AsrZt4zeXNwsH7QXHEJCFnCmqw9QzEoZTrNtHJHpNboBuVnYcoueZEJrP8OnUG3r
UjmopwKBgAqB2KYYMUqAOvYcBnEfLDmyZv9BTVNHbR2lKkMYqv5LlvDaBxVfilE0
2riO4p6BaAdvzXjKeRrGNEKoHNBpOSfYCOM16NjL8hIZB1CaV3WbT5oY+jp7Mzd5
7d56RZOE+ERK2uz/7JX9VSsM/LbH9pJibd4e8mikDS9ntciqOH/3
-----END RSA TESTING KEY-----`, "TESTING KEY", "PRIVATE KEY")))
testRSA2048, _ := x509.ParsePKCS1PrivateKey(block.Bytes)

fmt.Println("Private key bit size:", testRSA2048.N.BitLen())
}

// RSA is able to encrypt only a very limited amount of data. In order
// to encrypt reasonable amounts of data a hybrid scheme is commonly
// used: RSA is used to encrypt a key for a symmetric primitive like
Expand Down
24 changes: 24 additions & 0 deletions src/crypto/rsa/fips.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ func (opts *PSSOptions) saltLength() int {
// using bytes from rand. Most applications should use [crypto/rand.Reader] as
// rand.
func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte, opts *PSSOptions) ([]byte, error) {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return nil, err
}

if opts != nil && opts.Hash != 0 {
hash = opts.Hash
}
Expand Down Expand Up @@ -106,6 +110,10 @@ func SignPSS(rand io.Reader, priv *PrivateKey, hash crypto.Hash, digest []byte,
// The inputs are not considered confidential, and may leak through timing side
// channels, or if an attacker has control of part of the inputs.
func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts *PSSOptions) error {
if err := checkPublicKeySize(pub); err != nil {
return err
}

if boring.Enabled {
bkey, err := boringPublicKey(pub)
if err != nil {
Expand Down Expand Up @@ -152,6 +160,10 @@ func VerifyPSS(pub *PublicKey, hash crypto.Hash, digest []byte, sig []byte, opts
// The message must be no longer than the length of the public modulus minus
// twice the hash length, minus a further 2.
func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
if err := checkPublicKeySize(pub); err != nil {
return nil, err
}

defer hash.Reset()

if boring.Enabled && random == boring.RandReader {
Expand Down Expand Up @@ -191,6 +203,10 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext
}

func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, label []byte) ([]byte, error) {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return nil, err
}

if boring.Enabled {
k := priv.Size()
if len(ciphertext) > k ||
Expand Down Expand Up @@ -229,6 +245,10 @@ func decryptOAEP(hash, mgfHash hash.Hash, priv *PrivateKey, ciphertext []byte, l
// messages to signatures and identify the signed messages. As ever,
// signatures provide authenticity, not confidentiality.
func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed []byte) ([]byte, error) {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return nil, err
}

if boring.Enabled {
bkey, err := boringPrivateKey(priv)
if err != nil {
Expand Down Expand Up @@ -260,6 +280,10 @@ func SignPKCS1v15(random io.Reader, priv *PrivateKey, hash crypto.Hash, hashed [
// The inputs are not considered confidential, and may leak through timing side
// channels, or if an attacker has control of part of the inputs.
func VerifyPKCS1v15(pub *PublicKey, hash crypto.Hash, hashed []byte, sig []byte) error {
if err := checkPublicKeySize(pub); err != nil {
return err
}

if boring.Enabled {
bkey, err := boringPublicKey(pub)
if err != nil {
Expand Down
12 changes: 12 additions & 0 deletions src/crypto/rsa/pkcs1v15.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ type PKCS1v15DecryptOptions struct {
// WARNING: use of this function to encrypt plaintexts other than
// session keys is dangerous. Use RSA OAEP in new protocols.
func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, error) {
if err := checkPublicKeySize(pub); err != nil {
return nil, err
}

randutil.MaybeReadByte(random)

k := pub.Size()
Expand Down Expand Up @@ -90,6 +94,10 @@ func EncryptPKCS1v15(random io.Reader, pub *PublicKey, msg []byte) ([]byte, erro
// forge signatures as if they had the private key. See
// DecryptPKCS1v15SessionKey for a way of solving this problem.
func DecryptPKCS1v15(random io.Reader, priv *PrivateKey, ciphertext []byte) ([]byte, error) {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return nil, err
}

if boring.Enabled {
bkey, err := boringPrivateKey(priv)
if err != nil {
Expand Down Expand Up @@ -147,6 +155,10 @@ func DecryptPKCS1v15(random io.Reader, priv *PrivateKey, ciphertext []byte) ([]b
// - [1] RFC 3218, Preventing the Million Message Attack on CMS,
// https://www.rfc-editor.org/rfc/rfc3218.html
func DecryptPKCS1v15SessionKey(random io.Reader, priv *PrivateKey, ciphertext []byte, key []byte) error {
if err := checkPublicKeySize(&priv.PublicKey); err != nil {
return err
}

k := priv.Size()
if k-(len(key)+3+8) < 0 {
return ErrDecryption
Expand Down
37 changes: 18 additions & 19 deletions src/crypto/rsa/pkcs1v15_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,12 +54,14 @@ var decryptPKCS1v15Tests = []DecryptPKCS1v15Test{
}

func TestDecryptPKCS1v15(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")

decryptionFuncs := []func([]byte) ([]byte, error){
func(ciphertext []byte) (plaintext []byte, err error) {
return DecryptPKCS1v15(nil, rsaPrivateKey, ciphertext)
return DecryptPKCS1v15(nil, test512Key, ciphertext)
},
func(ciphertext []byte) (plaintext []byte, err error) {
return rsaPrivateKey.Decrypt(nil, ciphertext, nil)
return test512Key.Decrypt(nil, ciphertext, nil)
},
}

Expand Down Expand Up @@ -139,9 +141,10 @@ var decryptPKCS1v15SessionKeyTests = []DecryptPKCS1v15Test{
}

func TestEncryptPKCS1v15SessionKey(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")
for i, test := range decryptPKCS1v15SessionKeyTests {
key := []byte("FAIL")
err := DecryptPKCS1v15SessionKey(nil, rsaPrivateKey, decodeBase64(test.in), key)
err := DecryptPKCS1v15SessionKey(nil, test512Key, decodeBase64(test.in), key)
if err != nil {
t.Errorf("#%d error decrypting", i)
}
Expand All @@ -153,8 +156,9 @@ func TestEncryptPKCS1v15SessionKey(t *testing.T) {
}

func TestEncryptPKCS1v15DecrypterSessionKey(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")
for i, test := range decryptPKCS1v15SessionKeyTests {
plaintext, err := rsaPrivateKey.Decrypt(rand.Reader, decodeBase64(test.in), &PKCS1v15DecryptOptions{SessionKeyLen: 4})
plaintext, err := test512Key.Decrypt(rand.Reader, decodeBase64(test.in), &PKCS1v15DecryptOptions{SessionKeyLen: 4})
if err != nil {
t.Fatalf("#%d: error decrypting: %s", i, err)
}
Expand Down Expand Up @@ -196,12 +200,13 @@ var signPKCS1v15Tests = []signPKCS1v15Test{
}

func TestSignPKCS1v15(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")
for i, test := range signPKCS1v15Tests {
h := sha1.New()
h.Write([]byte(test.in))
digest := h.Sum(nil)

s, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.SHA1, digest)
s, err := SignPKCS1v15(nil, test512Key, crypto.SHA1, digest)
if err != nil {
t.Errorf("#%d %s", i, err)
}
Expand All @@ -214,29 +219,33 @@ func TestSignPKCS1v15(t *testing.T) {
}

func TestVerifyPKCS1v15(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")
for i, test := range signPKCS1v15Tests {
h := sha1.New()
h.Write([]byte(test.in))
digest := h.Sum(nil)

sig, _ := hex.DecodeString(test.out)

err := VerifyPKCS1v15(&rsaPrivateKey.PublicKey, crypto.SHA1, digest, sig)
err := VerifyPKCS1v15(&test512Key.PublicKey, crypto.SHA1, digest, sig)
if err != nil {
t.Errorf("#%d %s", i, err)
}
}
}

func TestOverlongMessagePKCS1v15(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")
ciphertext := decodeBase64("fjOVdirUzFoLlukv80dBllMLjXythIf22feqPrNo0YoIjzyzyoMFiLjAc/Y4krkeZ11XFThIrEvw\nkRiZcCq5ng==")
_, err := DecryptPKCS1v15(nil, rsaPrivateKey, ciphertext)
_, err := DecryptPKCS1v15(nil, test512Key, ciphertext)
if err == nil {
t.Error("RSA decrypted a message that was too long.")
}
}

func TestUnpaddedSignature(t *testing.T) {
t.Setenv("GODEBUG", "rsa1024min=0")

msg := []byte("Thu Dec 19 18:06:16 EST 2013\n")
// This base64 value was generated with:
// % echo Thu Dec 19 18:06:16 EST 2013 > /tmp/msg
Expand All @@ -246,14 +255,14 @@ func TestUnpaddedSignature(t *testing.T) {
// file.
expectedSig := decodeBase64("pX4DR8azytjdQ1rtUiC040FjkepuQut5q2ZFX1pTjBrOVKNjgsCDyiJDGZTCNoh9qpXYbhl7iEym30BWWwuiZg==")

sig, err := SignPKCS1v15(nil, rsaPrivateKey, crypto.Hash(0), msg)
sig, err := SignPKCS1v15(nil, test512Key, crypto.Hash(0), msg)
if err != nil {
t.Fatalf("SignPKCS1v15 failed: %s", err)
}
if !bytes.Equal(sig, expectedSig) {
t.Fatalf("signature is not expected value: got %x, want %x", sig, expectedSig)
}
if err := VerifyPKCS1v15(&rsaPrivateKey.PublicKey, crypto.Hash(0), msg, sig); err != nil {
if err := VerifyPKCS1v15(&test512Key.PublicKey, crypto.Hash(0), msg, sig); err != nil {
t.Fatalf("signature failed to verify: %s", err)
}
}
Expand All @@ -278,16 +287,6 @@ func TestShortSessionKey(t *testing.T) {
}
}

var rsaPrivateKey = parseKey(testingKey(`-----BEGIN RSA TESTING KEY-----
MIIBOgIBAAJBALKZD0nEffqM1ACuak0bijtqE2QrI/KLADv7l3kK3ppMyCuLKoF0
fd7Ai2KW5ToIwzFofvJcS/STa6HA5gQenRUCAwEAAQJBAIq9amn00aS0h/CrjXqu
/ThglAXJmZhOMPVn4eiu7/ROixi9sex436MaVeMqSNf7Ex9a8fRNfWss7Sqd9eWu
RTUCIQDasvGASLqmjeffBNLTXV2A5g4t+kLVCpsEIZAycV5GswIhANEPLmax0ME/
EO+ZJ79TJKN5yiGBRsv5yvx5UiHxajEXAiAhAol5N4EUyq6I9w1rYdhPMGpLfk7A
IU2snfRJ6Nq2CQIgFrPsWRCkV+gOYcajD17rEqmuLrdIRexpg8N1DOSXoJ8CIGlS
tAboUGBxTDq3ZroNism3DaMIbKPyYrAqhKov1h5V
-----END RSA TESTING KEY-----`))

func parsePublicKey(s string) *PublicKey {
p, _ := pem.Decode([]byte(s))
k, err := x509.ParsePKCS1PublicKey(p.Bytes)
Expand Down
Loading

0 comments on commit 10fb001

Please sign in to comment.