Skip to content
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

Add CreateDecrypter (RSA OAEP) support to Google Cloud KMS #238

Merged
merged 8 commits into from
May 19, 2023

Conversation

hslatman
Copy link
Member

No description provided.

@hslatman hslatman changed the title Add CreateDecrypter (RSA) support to Google Cloud KMS Add CreateDecrypter (RSA OAEP) support to Google Cloud KMS May 16, 2023
@hslatman hslatman changed the title Add CreateDecrypter (RSA OAEP) support to Google Cloud KMS Add CreateDecrypter (RSA OAEP) support to Google Cloud KMS (WIP) May 17, 2023
@hslatman hslatman requested a review from maraino May 17, 2023 22:23
Copy link
Contributor

@maraino maraino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not support any type of DecrypterOpts

kms/cloudkms/decrypter.go Outdated Show resolved Hide resolved
kms/cloudkms/decrypter.go Show resolved Hide resolved
Comment on lines 75 to 88
if ropts, ok := opts.(*rsa.OAEPOptions); ok && ropts != nil {
if len(ropts.Label) > 0 {
return nil, errors.New("cloudKMS does not support RSA-OAEP label")
}
switch ropts.Hash {
case crypto.SHA1, crypto.SHA256, crypto.SHA512:
break
default:
return nil, fmt.Errorf("cloudKMS does not support hash algorithm %q with RSA-OAEP", ropts.Hash)
}
}
if _, ok := opts.(*rsa.PKCS1v15DecryptOptions); ok {
return nil, errors.New("cloudKMS does not support PKCS #1 v1.5 decryption")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add a typed switch here, similar to rsa.Decrypt(). The difference would be that if opts == nil the default algorithm would be RSA-OAEP instead of PKCS #1 v1.5. We should explicitly say that in the comment.

Copy link
Member Author

@hslatman hslatman May 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that, but the code doesn't look cleaner then. The reason for that is that there can be typed nils, but also untyped nils. Both have to be handled to be able to verify the input.

The goal of the code is to show a more descriptive error message in case decryption fails if a user tries to use a parameter that we know will for sure result in an error. For example, when a label is used, the error is very generic: Error: error decrypting input: cloudKMS AsymmetricDecrypt failed: rpc error: code = InvalidArgument desc = Decryption failed.. Same is true if someone tries to use this with PKCS1 options. So we're not actually passing the options to GCP, because there's no way to do so. We're merely checking for them, and return an error for things that GCP doesn't support.

We could in fact improve the check even more by capturing the key type during preload, storing that, and verifying that the request decryption parameters match the keys properties.

kms/cloudkms/decrypter.go Show resolved Hide resolved
Comment on lines 69 to 89
// Decrypt decrypts ciphertext using the decryption key backed by Google Cloud KMS and returns
// the plaintext bytes. An error is returned when decryption fails. Google Cloud KMS only supports
// RSA keys with 2048, 3072 or 4096 bits and will always use OAEP. It supports SHA1, SHA256 and
// SHA512. Labels are not supported.
//
// Also see https://cloud.google.com/kms/docs/algorithms#asymmetric_encryption_algorithms.
func (d *Decrypter) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.DecrypterOpts) ([]byte, error) {
if ropts, ok := opts.(*rsa.OAEPOptions); ok && ropts != nil {
if len(ropts.Label) > 0 {
return nil, errors.New("cloudKMS does not support RSA-OAEP label")
}
switch ropts.Hash {
case crypto.SHA1, crypto.SHA256, crypto.SHA512:
break
default:
return nil, fmt.Errorf("cloudKMS does not support hash algorithm %q with RSA-OAEP", ropts.Hash)
}
}
if _, ok := opts.(*rsa.PKCS1v15DecryptOptions); ok {
return nil, errors.New("cloudKMS does not support PKCS #1 v1.5 decryption")
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can do something like the following:

func validateOAEPOptions(o *rsa.OAEPOptions) error {
	if len(o.Label) > 0 {
		return errors.New("cloudKMS does not support RSA-OAEP label")
	}
	switch o.Hash {
	case crypto.Hash(0), crypto.SHA1, crypto.SHA256, crypto.SHA512:
		return nil
	default:
		return fmt.Errorf("cloudKMS does not support hash algorithm %q with RSA-OAEP", ropts.Hash)
	}
}

func (d *Decrypter) Decrypt(rand io.Reader, ciphertext []byte, opts crypto.DecrypterOpts) ([]byte, error) {
	if opts == nil {
		opts = &rsa.OAEPOptions{}
	}

	switch o := opts.(type) {
	case *rsa.OAEPOptions:
		if err := validateOAEPOptions(o); err != nil {
			return nil, err
		}
	case *rsa.PKCS1v15DecryptOptions:
		return nil, errors.New("cloudKMS does not support PKCS #1 v1.5 decryption")
	default:
		return nil, errors.New("invalid options for Decrypt")
	}
	// ...
}

You can also do nothing in the case *rsa.OAEPOptions and do the validation after the switch.

The algorithm really depends on the key itself, so it makes sense to allow crypto.Hash(0) too.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed it in c726662. Note the additional o == nil case. It's required in a fairly specific usage pattern.

@hslatman hslatman changed the title Add CreateDecrypter (RSA OAEP) support to Google Cloud KMS (WIP) Add CreateDecrypter (RSA OAEP) support to Google Cloud KMS May 19, 2023
@hslatman hslatman requested a review from maraino May 19, 2023 22:07
Comment on lines 96 to 98
if o == nil { // var o *rsa.OAEPOptions; nothing to verify
break
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can do this in validateOAEPOptions.

Copy link
Contributor

@maraino maraino left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm

@hslatman hslatman merged commit 4677a47 into master May 19, 2023
@hslatman hslatman deleted the herman/cloudkms-decrypter branch May 19, 2023 22:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants