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

Allow non-cert public keys #965

Merged
merged 1 commit into from
Sep 30, 2016
Merged

Conversation

ecordell
Copy link
Contributor

This allows non-cert PEM encoded public keys to be loaded as delegation keys

@docker-jenkins
Copy link

Can one of the admins verify this patch?

if err != nil {
return "", fmt.Errorf("unable to parse pem encoded public key: %v", err)
}
_, ok := pub.(*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.

Use a type switch for clarity and ease of adding other key types in the future:

switch pub.(type) {
case *ecdsa.PublicKey:
    return data.ECDSAKey, nil
case *rsa.PublicKey:
    return data.RSAKey, nil
}

Copy link
Contributor

Choose a reason for hiding this comment

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

Non-blocking: We seem to support ED25519 for ParsePEMPrivateKey, although I guess we concatenate the private and public parts. I don't think it violates any existing spec to PEM-encode the public key, although I know it's not not supported by openssl or other software. Would it make sense to either support that here, or remove support in ParsePEMPrivateKey?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm for supporting it here as well - PEM's just a transport format.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@endophage Also a good review note - not sure why I didn't do that originally.

}
return "", fmt.Errorf("unkown public key format")
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 probably have a relevant error type for this somewhere.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I couldn't find one but I'm happy to switch it if you can point me to it.

Copy link
Contributor

Choose a reason for hiding this comment

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

In tuf/signed/errors.go we have ErrInvalidKeyType which seems appropriate. It's used to indicate it the key type is invalid/unrecognized in the context of key types Notary accepts, not necessarily that the key is universally invalid.

@@ -252,9 +252,31 @@ func ParsePEMPublicKey(pubKeyBytes []byte) (data.PublicKey, error) {
return nil, fmt.Errorf("invalid certificate: %v", err)
}
return CertToKey(cert), nil
case "PUBLIC KEY":
keyType, err := keyTypeForPublicKey(pemBlock.Bytes)
Copy link
Contributor

Choose a reason for hiding this comment

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

Would it make sense to move this entire case block into a new constructor for data.PublicKey objects that takes a []byte (in this case pemBlock.Bytes) and returns (PublicKey, error)?

Copy link
Contributor Author

@ecordell ecordell Sep 22, 2016

Choose a reason for hiding this comment

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

That seems like a good refactor to me; I assume the same for ParsePEMPrivateKey?

I was going to bring this up elsewhere but it seems relevant here: I think it's confusing that if you pass in a cert to ParsePEMPublicKey you get back a data.PublicKey object containing the cert bytes in the Public field instead of the parsed out public key bytes.

I was planning to read through a bit more before suggesting a change, but personally I like the idea of a flow like: certs get loaded/verified -> translated to plain keys -> tuf only deals with plain keys.

This seems like a very clean separation to me; the only issue would be that you would lose the property that certs get verified right before they're used every time. Perhaps it would make sense, after loading the certs into memory, to keep around a map of parsedKeys -> originatingCerts so that at various well-defined points during verification, you can do something like reverifyCerts(currentlyReleventKeyList) to find the certs you're using and check them again.

Just a couple of thoughts I've had on making the flow of notary easier to understand. I've just rambled a bit too much for an issue comment, so I'll pull this out into an issue later today...

Copy link
Contributor

Choose a reason for hiding this comment

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

I agree it's a little messy and would be good to clean up. Let's discuss how to approach it in the issue you file.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@cyli cyli 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's a minor lint error. It'd also be awesome to add a test case for a PEM encoded file of a public key that is not supported (if we don't want to support ED25519, for example, that might be a good one. :)

Thanks for adding this!

@@ -814,6 +814,52 @@ func generateRootKeyIDs(r *data.SignedRoot) {
}
}

func TestParsePEMPublicKey(t *testing.T) {
gun := "notary"
pass := func(keyName, alias string, createNew bool, attempts int) (passphrase string, giveup bool, err error) {
Copy link
Contributor

@cyli cyli Sep 21, 2016

Choose a reason for hiding this comment

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

Non-blocking: I think you can use passphrase.ConstantRetriever("password") for this, unless you don't want the extra import.

if err != nil {
return "", fmt.Errorf("unable to parse pem encoded public key: %v", err)
}
_, ok := pub.(*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.

Non-blocking: We seem to support ED25519 for ParsePEMPrivateKey, although I guess we concatenate the private and public parts. I don't think it violates any existing spec to PEM-encode the public key, although I know it's not not supported by openssl or other software. Would it make sense to either support that here, or remove support in ParsePEMPrivateKey?

@ecordell
Copy link
Contributor Author

Addressed review comments.

@endophage held off on public key refactoring because of #977

@cyli Didn't end up adding ed25519 support because the x509 library doesn't recognize it as a valid PEM public key type. If it's something that should be supported can always wrap the x509 lib and add ed25519 support.

@cyli
Copy link
Contributor

cyli commented Sep 26, 2016

jenkins, test this please

Copy link
Contributor

@cyli cyli left a comment

Choose a reason for hiding this comment

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

Didn't end up adding ed25519 support because the x509 library doesn't recognize it as a valid PEM public key type. If it's something that should be supported can always wrap the x509 lib and add ed25519 support.

I think we use ED25519ToPrivateKey to unmarshal the private key - we do our own weird thing where basically the bytes are the public key bytes + the private key bytes, which is how we unmarshal the private key. I think the same thing can be done to parse out the public bytes (not using the x509 lib). This is non-blocking though - it's not like we supported importing ED25519 keys before. :)

I'm ok with addressing #977 in a separate PR - this LGTM aside from the one extra function that can be removed.

@@ -1007,12 +1003,79 @@ func generateExpiredTestingCertificate(rootKey data.PrivateKey, gun string) (*x5
return cryptoservice.GenerateCertificate(rootKey, gun, startTime, startTime.AddDate(1, 0, 0))
}

// Helper function for explicitly generating key IDs and unexported fields for equality testing
func generateRootKeyIDs(r *data.SignedRoot) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure if this function is still used anywhere - should it be removed? Although you do bring up a good point; it would be good to useful to have equality test functions for some of our data structures that have unexported fields at some point.

@cyli
Copy link
Contributor

cyli commented Sep 26, 2016

jenkins, test this please

Copy link
Contributor

@cyli cyli left a comment

Choose a reason for hiding this comment

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

Thanks for the update! :) LGTM

@cyli
Copy link
Contributor

cyli commented Sep 26, 2016

(Hmm... you can't edit the text of your previous review status comment :P sorry for the spam!)

Copy link
Contributor

@riyazdf riyazdf left a comment

Choose a reason for hiding this comment

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

some small nits, but overall LGTM! Thank you @ecordell :)


// unsupported key type
unsupportedPemBytes := pem.EncodeToMemory(&pem.Block{
Type: "UNSUPORTED KEY",
Copy link
Contributor

Choose a reason for hiding this comment

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

nit typo: UNSUPPORTED

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 this case errors because pem.Decode fails, not because of the type-switch.

This case is great, but It would be awesome if we could add another test case with a valid type that ParsePEMPublicKey does not support, like CERTIFICATE REQUEST or PRIVATE KEY

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated!

Signed-off-by: Evan Cordell <cordell.evan@gmail.com>
@cyli
Copy link
Contributor

cyli commented Sep 30, 2016

jenkins, test this please

@riyazdf riyazdf merged commit 0dba98d into notaryproject:master Sep 30, 2016
@riyazdf
Copy link
Contributor

riyazdf commented Sep 30, 2016

thanks @ecordell for updating this, glad to have this feature :)

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.

6 participants