Skip to content

proposal: crypto/rsa: support RSA/ECB/OAEPWithSHA-256AndMGF1Padding #72781

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
javen-yan opened this issue Mar 11, 2025 · 2 comments
Closed

proposal: crypto/rsa: support RSA/ECB/OAEPWithSHA-256AndMGF1Padding #72781

javen-yan opened this issue Mar 11, 2025 · 2 comments
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal Proposal-Crypto Proposal related to crypto packages or other security issues
Milestone

Comments

@javen-yan
Copy link

Proposal Details

Hello, everyone.

Currently, I've encountered an issue where the RSA encryption algorithms are inconsistent across different languages during the project integration process. To address this problem, I had no choice but to modify some code in the crypto basic library.

Process:

  1. When integrating with a Java implementation of the RSA encryption program, it can be found that the Java implementation uses the RSA/ECB/OAEPWithSHA-256AndMGF1Padding method.
  2. When I used the private key for decryption, I found that the DecryptOAEP method in crypto only provides a single input parameter for the Hash algorithm. When I used sha256.New() to create the hash, the lack of MGF1Padding caused the decryption to fail.
    Code:
func DecryptOAEP(hash hash.Hash, random io.Reader, priv *PrivateKey, ciphertext []byte, label []byte) ([]byte, error) {
        // hash, hash  reuse common Hash 
	return decryptOAEP(hash, hash, random, priv, ciphertext, label)
}
  1. Later, I discovered that PrivateKey provides a Decrypt method, which can be implemented through the extensible opts crypto.DecrypterOpts, thus solving my problem.
func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error) {
	if opts == nil {
		return DecryptPKCS1v15(rand, priv, ciphertext)
	}

	switch opts := opts.(type) {
	case *OAEPOptions:
		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 {
			plaintext = make([]byte, l)
			if _, err := io.ReadFull(rand, plaintext); err != nil {
				return nil, err
			}
			if err := DecryptPKCS1v15SessionKey(rand, priv, ciphertext, plaintext); err != nil {
				return nil, err
			}
			return plaintext, nil
		} else {
			return DecryptPKCS1v15(rand, priv, ciphertext)
		}

	default:
		return nil, errors.New("crypto/rsa: invalid options for Decrypt")
	}
}
  1. Originally, I thought I was just one step away from success. However, here comes the problem when I implemented the Golang encryption. During decryption, we used two hash algorithms as factors for decryption to implement RSA/ECB/OAEPWithSHA-256AndMGF1Padding, and the official also provides standard APIs.
  2. During encryption, there should be a similar interface to support RSA/ECB/OAEPWithSHA-256AndMGF1Padding. Unfortunately, I couldn't find such a method.

Optimization suggestion:

Can you geniuses provide an external interface that exposes a method similar to func (priv *PrivateKey) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.DecrypterOpts) (plaintext []byte, err error)?

The following is the code I used to solve my problem. I hope it can be of some help. Of course, there is still much room for improvement.

type EncrypterOpts any

func (pub *PublicKey) Encrypt(rand io.Reader, msg []byte, opts EncrypterOpts) (plaintext []byte, err error) {
	if opts == nil {
		return EncryptPKCS1v15(rand, pub, msg)
	}
	switch opts := opts.(type) {
	case *OAEPOptions:
		if opts.MGFHash == 0 {
			return encryptOAEP(opts.Hash.New(), opts.Hash.New(), rand, pub, msg, opts.Label)
		} else {
			return encryptOAEP(opts.Hash.New(), opts.MGFHash.New(), rand, pub, msg, opts.Label)
		}
	default:
		return nil, errors.New("crypto/rsa: invalid options for Encrypt")
	}
}

func encryptOAEP(hash, MGFHash hash.Hash, random io.Reader, pub *PublicKey, msg []byte, label []byte) ([]byte, error) {
	// Note that while we don't commit to deterministic execution with respect
	// to the random stream, we also don't apply MaybeReadByte, so per Hyrum's
	// Law it's probably relied upon by some. It's a tolerable promise because a
	// well-specified number of random bytes is included in the ciphertext, in a
	// well-specified way.

	if err := checkPub(pub); err != nil {
		return nil, err
	}
	hash.Reset()
	k := pub.Size()
	if len(msg) > k-2*hash.Size()-2 {
		return nil, ErrMessageTooLong
	}

	hash.Write(label)
	lHash := hash.Sum(nil)
	hash.Reset()

	em := make([]byte, k)
	seed := em[1 : 1+hash.Size()]
	db := em[1+hash.Size():]

	copy(db[0:hash.Size()], lHash)
	db[len(db)-len(msg)-1] = 1
	copy(db[len(db)-len(msg):], msg)

	_, err := io.ReadFull(random, seed)
	if err != nil {
		return nil, err
	}

	mgf1XOR(db, MGFHash, seed)
	mgf1XOR(seed, MGFHash, db)

	return encrypt(pub, em)
}

I'm looking forward to your replies.

@gopherbot gopherbot added this to the Proposal milestone Mar 11, 2025
@ianlancetaylor ianlancetaylor changed the title proposal: import/path: crypto/rsa support RSA/ECB/OAEPWithSHA-256AndMGF1Padding proposal: crypto/rsa: support RSA/ECB/OAEPWithSHA-256AndMGF1Padding Mar 11, 2025
@ianlancetaylor ianlancetaylor added the Proposal-Crypto Proposal related to crypto packages or other security issues label Mar 11, 2025
@ianlancetaylor ianlancetaylor moved this to Incoming in Proposals Mar 11, 2025
@ianlancetaylor
Copy link
Member

CC @golang/security

@gabyhelp
Copy link

@gabyhelp gabyhelp added the LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool label Mar 11, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LibraryProposal Issues describing a requested change to the Go standard library or x/ libraries, but not to a tool Proposal Proposal-Crypto Proposal related to crypto packages or other security issues
Projects
Status: Incoming
Development

No branches or pull requests

5 participants