]> Cypherpunks.ru repositories - gostls13.git/commitdiff
crypto: allow hash.Hash for OAEP and MGF1 to be specified independently
authorAleks Rudzitis <arudzitis@stripe.com>
Thu, 21 Jul 2022 18:19:42 +0000 (11:19 -0700)
committerGopher Robot <gobot@golang.org>
Wed, 9 Nov 2022 23:51:34 +0000 (23:51 +0000)
crypto/rsa assumes RSA OAEP uses the same hash to be used for both the label
and the mask generation function. However, implementations in other languages,
such as Java and Python, allow these parameters to be specified independently.

This change allows the MGF hash to be specified independently for decrypt
operations in order to allow decrypting ciphertexts generated in other
environments.

Fixes: #19974
Change-Id: If453d628f0da354ceb3b52863f30087471670f7b
Reviewed-on: https://go-review.googlesource.com/c/go/+/418874
Auto-Submit: Andrew Bonventre <andybons@golang.org>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Russ Cox <rsc@golang.org>
Reviewed-by: Robert Griesemer <gri@google.com>
Reviewed-by: Andrew Bonventre <andybons@golang.org>
Run-TryBot: Andrew Bonventre <andybons@golang.org>

api/next/19974.txt [new file with mode: 0644]
src/crypto/internal/boring/notboring.go
src/crypto/internal/boring/rsa.go
src/crypto/rsa/rsa.go
src/crypto/rsa/rsa_test.go

diff --git a/api/next/19974.txt b/api/next/19974.txt
new file mode 100644 (file)
index 0000000..22893fa
--- /dev/null
@@ -0,0 +1 @@
+pkg crypto/rsa, type OAEPOptions struct, MGFHash crypto.Hash #19974
index e8eb76e1bb136dc72346b73ba72ea401df144fef..2fa5eaf30f7d2a5cbe1f9e1b391f411b930ced13 100644 (file)
@@ -73,7 +73,7 @@ func VerifyECDSA(pub *PublicKeyECDSA, hash []byte, sig []byte) bool {
 type PublicKeyRSA struct{ _ int }
 type PrivateKeyRSA struct{ _ int }
 
-func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
        panic("boringcrypto: not available")
 }
 func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
@@ -82,7 +82,7 @@ func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
 func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
        panic("boringcrypto: not available")
 }
-func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
        panic("boringcrypto: not available")
 }
 func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
index a1f85591a77d265358fe9422bac89d5bf4d099f2..fa693ea3198da6b4c61ad93227df99bca9b548a7 100644 (file)
@@ -109,7 +109,7 @@ func (k *PrivateKeyRSA) withKey(f func(*C.GO_RSA) C.int) C.int {
 }
 
 func setupRSA(withKey func(func(*C.GO_RSA) C.int) C.int,
-       padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+       padding C.int, h, mgfHash hash.Hash, label []byte, saltLen int, ch crypto.Hash,
        init func(*C.GO_EVP_PKEY_CTX) C.int) (pkey *C.GO_EVP_PKEY, ctx *C.GO_EVP_PKEY_CTX, err error) {
        defer func() {
                if err != nil {
@@ -148,9 +148,16 @@ func setupRSA(withKey func(func(*C.GO_RSA) C.int) C.int,
                if md == nil {
                        return nil, nil, errors.New("crypto/rsa: unsupported hash function")
                }
+               mgfMD := hashToMD(mgfHash)
+               if mgfMD == nil {
+                       return nil, nil, errors.New("crypto/rsa: unsupported hash function")
+               }
                if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_oaep_md(ctx, md) == 0 {
                        return nil, nil, fail("EVP_PKEY_set_rsa_oaep_md")
                }
+               if C._goboringcrypto_EVP_PKEY_CTX_set_rsa_mgf1_md(ctx, mgfMD) == 0 {
+                       return nil, nil, fail("EVP_PKEY_set_rsa_mgf1_md")
+               }
                // ctx takes ownership of label, so malloc a copy for BoringCrypto to free.
                clabel := (*C.uint8_t)(C._goboringcrypto_OPENSSL_malloc(C.size_t(len(label))))
                if clabel == nil {
@@ -180,12 +187,12 @@ func setupRSA(withKey func(func(*C.GO_RSA) C.int) C.int,
 }
 
 func cryptRSA(withKey func(func(*C.GO_RSA) C.int) C.int,
-       padding C.int, h hash.Hash, label []byte, saltLen int, ch crypto.Hash,
+       padding C.int, h, mgfHash hash.Hash, label []byte, saltLen int, ch crypto.Hash,
        init func(*C.GO_EVP_PKEY_CTX) C.int,
        crypt func(*C.GO_EVP_PKEY_CTX, *C.uint8_t, *C.size_t, *C.uint8_t, C.size_t) C.int,
        in []byte) ([]byte, error) {
 
-       pkey, ctx, err := setupRSA(withKey, padding, h, label, saltLen, ch, init)
+       pkey, ctx, err := setupRSA(withKey, padding, h, mgfHash, label, saltLen, ch, init)
        if err != nil {
                return nil, err
        }
@@ -203,28 +210,28 @@ func cryptRSA(withKey func(func(*C.GO_RSA) C.int) C.int,
        return out[:outLen], nil
 }
 
-func DecryptRSAOAEP(h hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
-       return cryptRSA(priv.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, decryptInit, decrypt, ciphertext)
+func DecryptRSAOAEP(h, mgfHash hash.Hash, priv *PrivateKeyRSA, ciphertext, label []byte) ([]byte, error) {
+       return cryptRSA(priv.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, mgfHash, label, 0, 0, decryptInit, decrypt, ciphertext)
 }
 
-func EncryptRSAOAEP(h hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
-       return cryptRSA(pub.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, label, 0, 0, encryptInit, encrypt, msg)
+func EncryptRSAOAEP(h, mgfHash hash.Hash, pub *PublicKeyRSA, msg, label []byte) ([]byte, error) {
+       return cryptRSA(pub.withKey, C.GO_RSA_PKCS1_OAEP_PADDING, h, mgfHash, label, 0, 0, encryptInit, encrypt, msg)
 }
 
 func DecryptRSAPKCS1(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
-       return cryptRSA(priv.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
+       return cryptRSA(priv.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
 }
 
 func EncryptRSAPKCS1(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
-       return cryptRSA(pub.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg)
+       return cryptRSA(pub.withKey, C.GO_RSA_PKCS1_PADDING, nil, nil, nil, 0, 0, encryptInit, encrypt, msg)
 }
 
 func DecryptRSANoPadding(priv *PrivateKeyRSA, ciphertext []byte) ([]byte, error) {
-       return cryptRSA(priv.withKey, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
+       return cryptRSA(priv.withKey, C.GO_RSA_NO_PADDING, nil, nil, nil, 0, 0, decryptInit, decrypt, ciphertext)
 }
 
 func EncryptRSANoPadding(pub *PublicKeyRSA, msg []byte) ([]byte, error) {
-       return cryptRSA(pub.withKey, C.GO_RSA_NO_PADDING, nil, nil, 0, 0, encryptInit, encrypt, msg)
+       return cryptRSA(pub.withKey, C.GO_RSA_NO_PADDING, nil, nil, nil, 0, 0, encryptInit, encrypt, msg)
 }
 
 // These dumb wrappers work around the fact that cgo functions cannot be used as values directly.
index c941124fb26a10158e6d132c48ddc3932531f48b..9c57595dd1abdf683e126153251d30db2fec440b 100644 (file)
@@ -68,6 +68,11 @@ func (pub *PublicKey) Equal(x crypto.PublicKey) bool {
 type OAEPOptions struct {
        // Hash is the hash function that will be used when generating the mask.
        Hash crypto.Hash
+
+       // MGFHash is the hash function used for MGF1.
+       // If zero, Hash is used instead.
+       MGFHash crypto.Hash
+
        // Label is an arbitrary byte string that must be equal to the value
        // used when encrypting.
        Label []byte
@@ -160,7 +165,11 @@ func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.D
 
        switch opts := opts.(type) {
        case *OAEPOptions:
-               return DecryptOAEP(opts.Hash.New(), rand, priv, ciphertext, opts.Label)
+               if opts.MGFHash == 0 {
+                       return decryptOAEP(opts.Hash.New(), opts.Hash.New(), rand, priv, ciphertext, opts.Label)
+               } else {
+                       return decryptOAEP(opts.Hash.New(), opts.MGFHash.New(), rand, priv, ciphertext, opts.Label)
+               }
 
        case *PKCS1v15DecryptOptions:
                if l := opts.SessionKeyLen; l > 0 {
@@ -458,7 +467,7 @@ func EncryptOAEP(hash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, l
                if err != nil {
                        return nil, err
                }
-               return boring.EncryptRSAOAEP(hash, bkey, msg, label)
+               return boring.EncryptRSAOAEP(hash, hash, bkey, msg, label)
        }
        boring.UnreachableExceptTests()
 
@@ -651,6 +660,10 @@ func decryptAndCheck(random io.Reader, priv *PrivateKey, c *big.Int) (m *big.Int
 // The label parameter must match the value given when encrypting. See
 // EncryptOAEP for details.
 func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) ([]byte, error) {
+       return decryptOAEP(hash, hash, random, priv, ciphertext, label)
+}
+
+func decryptOAEP(hash, mgfHash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) ([]byte, error) {
        if err := checkPub(&priv.PublicKey); err != nil {
                return nil, err
        }
@@ -665,7 +678,7 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext
                if err != nil {
                        return nil, err
                }
-               out, err := boring.DecryptRSAOAEP(hash, bkey, ciphertext, label)
+               out, err := boring.DecryptRSAOAEP(hash, mgfHash, bkey, ciphertext, label)
                if err != nil {
                        return nil, ErrDecryption
                }
@@ -691,8 +704,8 @@ func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext
        seed := em[1 : hash.Size()+1]
        db := em[hash.Size()+1:]
 
-       mgf1XOR(seed, hash, db)
-       mgf1XOR(db, hash, seed)
+       mgf1XOR(seed, mgfHash, db)
+       mgf1XOR(db, mgfHash, seed)
 
        lHash2 := db[0:hash.Size()]
 
index 766d9a954f8d13f721f786372016990ade2d2a5a..99b2cf5ae84801bf3c065c89ada74d589653b1fc 100644 (file)
@@ -7,6 +7,7 @@ package rsa
 import (
        "bytes"
        "crypto"
+       "crypto/internal/boring"
        "crypto/rand"
        "crypto/sha1"
        "crypto/sha256"
@@ -15,8 +16,6 @@ import (
        "testing"
 )
 
-import "crypto/internal/boring"
-
 func TestKeyGeneration(t *testing.T) {
        for _, size := range []int{128, 1024, 2048, 3072} {
                priv, err := GenerateKey(rand.Reader, size)
@@ -303,6 +302,31 @@ func TestDecryptOAEP(t *testing.T) {
        }
 }
 
+func Test2DecryptOAEP(t *testing.T) {
+       random := rand.Reader
+
+       msg := []byte{0xed, 0x36, 0x90, 0x8d, 0xbe, 0xfc, 0x35, 0x40, 0x70, 0x4f, 0xf5, 0x9d, 0x6e, 0xc2, 0xeb, 0xf5, 0x27, 0xae, 0x65, 0xb0, 0x59, 0x29, 0x45, 0x25, 0x8c, 0xc1, 0x91, 0x22}
+       in := []byte{0x72, 0x26, 0x84, 0xc9, 0xcf, 0xd6, 0xa8, 0x96, 0x04, 0x3e, 0x34, 0x07, 0x2c, 0x4f, 0xe6, 0x52, 0xbe, 0x46, 0x3c, 0xcf, 0x79, 0x21, 0x09, 0x64, 0xe7, 0x33, 0x66, 0x9b, 0xf8, 0x14, 0x22, 0x43, 0xfe, 0x8e, 0x52, 0x8b, 0xe0, 0x5f, 0x98, 0xef, 0x54, 0xac, 0x6b, 0xc6, 0x26, 0xac, 0x5b, 0x1b, 0x4b, 0x7d, 0x2e, 0xd7, 0x69, 0x28, 0x5a, 0x2f, 0x4a, 0x95, 0x89, 0x6c, 0xc7, 0x53, 0x95, 0xc7, 0xd2, 0x89, 0x04, 0x6f, 0x94, 0x74, 0x9b, 0x09, 0x0d, 0xf4, 0x61, 0x2e, 0xab, 0x48, 0x57, 0x4a, 0xbf, 0x95, 0xcb, 0xff, 0x15, 0xe2, 0xa0, 0x66, 0x58, 0xf7, 0x46, 0xf8, 0xc7, 0x0b, 0xb5, 0x1e, 0xa7, 0xba, 0x36, 0xce, 0xdd, 0x36, 0x41, 0x98, 0x6e, 0x10, 0xf9, 0x3b, 0x70, 0xbb, 0xa1, 0xda, 0x00, 0x40, 0xd5, 0xa5, 0x3f, 0x87, 0x64, 0x32, 0x7c, 0xbc, 0x50, 0x52, 0x0e, 0x4f, 0x21, 0xbd}
+
+       n := new(big.Int)
+       d := new(big.Int)
+       n.SetString(testEncryptOAEPData[0].modulus, 16)
+       d.SetString(testEncryptOAEPData[0].d, 16)
+       priv := new(PrivateKey)
+       priv.PublicKey = PublicKey{N: n, E: testEncryptOAEPData[0].e}
+       priv.D = d
+       sha1 := crypto.SHA1
+       sha256 := crypto.SHA256
+
+       out, err := priv.Decrypt(random, in, &OAEPOptions{MGFHash: sha1, Hash: sha256})
+
+       if err != nil {
+               t.Errorf("error: %s", err)
+       } else if !bytes.Equal(out, msg) {
+               t.Errorf("bad result %#v (want %#v)", out, msg)
+       }
+}
+
 func TestEncryptDecryptOAEP(t *testing.T) {
        sha256 := sha256.New()
        n := new(big.Int)