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

Fix PEM decoding issue for some KMS keys #175

Merged
merged 3 commits into from
Mar 23, 2021
Merged

Conversation

Kay-Zee
Copy link
Member

@Kay-Zee Kay-Zee commented Mar 21, 2021

Closes: N/A

Description

Was running into a PEM decode issue when working with some KMS keys. Kept getting cryptokms: failed to parse PEM public key: input has incorrect ECDSA_P256 key size.

The cause of this is sometimes, when calling .Bytes() on the X and Y points after decoding the PEM key, the corresponding byte slices were not the full 32 bytes, which was expected by the next function (expecting ECDSA_P256 keys to have a raw key of length 64)

This adds left padding (since the bytes are returned big endian) up to the expected length for ECDSA_P256 only. Can add other known lengths as we start to use them more.


For contributor use:

  • Targeted PR against master branch
  • Linked to Github issue with discussion and accepted design OR link to spec that describes this work.
  • Code follows the standards mentioned here.
  • Updated relevant documentation
  • Re-reviewed Files changed in the Github PR explorer
  • Added appropriate labels

@turbolent turbolent requested a review from tarakby March 22, 2021 17:06
@turbolent
Copy link
Member

@tarakby Can you please have a look here?

Copy link
Contributor

@tarakby tarakby left a comment

Choose a reason for hiding this comment

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

I think there a few changes needed, some of them are related to the original function that existed before and I didn't notice.
Happy to help make the changes.

crypto/crypto.go Outdated
@@ -257,11 +267,31 @@ func DecodePublicKeyPEM(sigAlgo SignatureAlgorithm, s string) (PublicKey, error)
}

goPublicKey := publicKey.(*ecdsa.PublicKey)
Copy link
Contributor

Choose a reason for hiding this comment

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

I haven't noticed this before, we should test the type assertion here as other types of public keys may be returned.

goPublicKey, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
   return PublicKey{}, fmt.Error("only ECDSA public keys are supported")
}

crypto/crypto.go Outdated
var expectedPointByteLength = map[SignatureAlgorithm]int{
crypto.ECDSAP256: 32,
}

// DecodePublicKeyHex decodes a PEM public key with the given signature algorithm.
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
// DecodePublicKeyHex decodes a PEM public key with the given signature algorithm.
// DecodePublicKeyHex decodes a PEM ECDSA public key with the given curve.

crypto/crypto.go Outdated
var expectedPointByteLength = map[SignatureAlgorithm]int{
crypto.ECDSAP256: 32,
}

// DecodePublicKeyHex decodes a PEM public key with the given signature algorithm.
func DecodePublicKeyPEM(sigAlgo SignatureAlgorithm, s string) (PublicKey, error) {
block, _ := pem.Decode([]byte(s))
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we shouldn't have trailing data here.

Suggested change
block, _ := pem.Decode([]byte(s))
block, rest := pem.Decode([]byte(s))
if len(rest)>0 {
return PublicKey{}, fmt.Errorf("crypto: failed to parse PEM string: %w", err)
}

goPublicKey.X.Bytes(),
goPublicKey.Y.Bytes()...,
)
xBytes := goPublicKey.X.Bytes()
Copy link
Contributor

Choose a reason for hiding this comment

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

using ParsePKIXPublicKey only works in the case of curve P256. We will need to write a local ParsePKIXPublicKey function that supports secp256k1. I can help writing it.

crypto/crypto.go Outdated
)
xBytes := goPublicKey.X.Bytes()
yBytes := goPublicKey.Y.Bytes()
expectedLength := expectedPointByteLength[sigAlgo]
Copy link
Contributor

Choose a reason for hiding this comment

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

It's better to get that length this way:

Suggested change
expectedLength := expectedPointByteLength[sigAlgo]
expectedLength := bitsToBytes(goPublicKey.Params().P.BitLen())

With bitsToBytes the function converting bit size into byte size:

func bitsToBytes(bits int) int {
	return (bits + 7) >> 3
}

crypto/crypto.go Outdated
yBytes := goPublicKey.Y.Bytes()
expectedLength := expectedPointByteLength[sigAlgo]
var rawPublicKey []byte
if expectedLength > 0 {
Copy link
Contributor

Choose a reason for hiding this comment

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

We can remove this check and pad the expected length in all cases.

crypto/crypto.go Outdated
// we make sure to left pad it to the right length. This is a mapping of the
// signature algorithms to the expected length of the byte slices representing
// the X and Y points
var expectedPointByteLength = map[SignatureAlgorithm]int{
Copy link
Contributor

@tarakby tarakby Mar 22, 2021

Choose a reason for hiding this comment

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

Eventually, we won't need the map.

@Kay-Zee Kay-Zee merged commit 878e5e5 into master Mar 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants