-
Notifications
You must be signed in to change notification settings - Fork 0
Add crypto.Signer support for KMS/HSM keys #6
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
base: main
Are you sure you want to change the base?
Conversation
3b7da22 to
4a22109
Compare
|
I removed integration with Reviewable on this repo |
504881c to
d878cf8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR enables support for hardware security modules (HSM) and cloud Key Management Systems (KMS) by allowing the use of crypto.Signer interface implementations instead of requiring concrete *rsa.PrivateKey or *ecdsa.PrivateKey types.
Changes:
- Updated key type validation to check public key type instead of private key type, enabling KMS/HSM signer support
- Implemented custom JWT signing fallback using
crypto.Signerinterface for keys that aren't concrete private key types - Added ECDSA signature format conversion from ASN.1 DER to JWT's raw R||S format
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| service_provider.go | Updated key validation to check public key type instead of private key type |
| samlsp/session_jwt.go | Added custom JWT signing for crypto.Signer implementations with ECDSA signature conversion |
| samlsp/request_tracker_jwt.go | Added fallback to custom signing for crypto.Signer implementations |
| samlsp/new.go | Updated default signing method selection to check public key type |
| samlsp/middleware_test.go | Added comprehensive end-to-end tests using mock crypto.Signer |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: | ||
| return token.SignedString(c.Key) | ||
| default: | ||
| return signJWTWithCryptoSigner(token, c.Key, c.SigningMethod) |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type switch checks for concrete private key types that the jwt library can handle, but doesn't verify that c.Key implements crypto.Signer before falling through to signJWTWithCryptoSigner. Consider adding a type assertion to crypto.Signer in the default case to provide a clearer error message if an unsupported key type is passed.
| return signJWTWithCryptoSigner(token, c.Key, c.SigningMethod) | |
| signer, ok := c.Key.(crypto.Signer) | |
| if !ok || signer == nil { | |
| return "", fmt.Errorf("unsupported key type %T: expected a non-nil crypto.Signer", c.Key) | |
| } | |
| return signJWTWithCryptoSigner(token, signer, c.SigningMethod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is not required as the c.key is defined as a crypto.signer and golang type system will make sure of that. Doing that will be redundant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type system doesn't enforce that c.Key is non-nil. If there's a check somewhere that would catch that, that's fine, but it's probably best to return an error instead of allowing a potential nil dereference panic.
What really matters is whether your change would cause a downgrade. If previously the user would not encounter a panic when providing a nil key/signer and after your changes the user would encounter a panic, that is not okay. If, on the other hand, a user would have encountered a panic before your changes, it's acceptable for them to encounter a panic after your changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ok, added the nil check
| // Check if key is a concrete private key type that jwt library can handle directly. | ||
| // For crypto.Signer implementations (KMS/HSM), use custom signing. | ||
| switch s.Key.(type) { | ||
| case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: |
Copilot
AI
Jan 22, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type switch checks for concrete private key types that the jwt library can handle, but doesn't verify that s.Key implements crypto.Signer before falling through to signJWTWithCryptoSigner. Consider adding a type assertion to crypto.Signer in the default case to provide a clearer error message if an unsupported key type is passed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This check is not required as the c.key is defined as a crypto.signer and golang type system will make sure of that. Doing that will be redundant
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type system doesn't enforce that c.Key is non-nil. If there's a check somewhere that would catch that, that's fine, but it's probably best to return an error instead of allowing a potential nil dereference panic.
What really matters is whether your change would cause a downgrade. If previously the user would not encounter a panic when providing a nil key/signer and after your changes the user would encounter a panic, that is not okay. If, on the other hand, a user would have encountered a panic before your changes, it's acceptable for them to encounter a panic after your changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
added the nil check
d878cf8 to
9ad3a32
Compare
service_provider.go
Outdated
| if _, ok := sp.Key.(*ecdsa.PrivateKey); !ok { | ||
| return nil, fmt.Errorf("signature method %s requires a key of type ecdsa.PrivateKey, not %T", sp.SignatureMethod, sp.Key) | ||
| if _, ok := pubKey.(*ecdsa.PublicKey); !ok { | ||
| return nil, fmt.Errorf("signature method %s requires an ECDSA key, got %T", sp.SignatureMethod, pubKey) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a minor nit, but the old error messages used to specify the expected types for the public keys. That is useful and should probably be preserved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reverted back to old error message
| case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: | ||
| return token.SignedString(c.Key) | ||
| default: | ||
| return signJWTWithCryptoSigner(token, c.Key, c.SigningMethod) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type system doesn't enforce that c.Key is non-nil. If there's a check somewhere that would catch that, that's fine, but it's probably best to return an error instead of allowing a potential nil dereference panic.
What really matters is whether your change would cause a downgrade. If previously the user would not encounter a panic when providing a nil key/signer and after your changes the user would encounter a panic, that is not okay. If, on the other hand, a user would have encountered a panic before your changes, it's acceptable for them to encounter a panic after your changes.
| // Check if key is a concrete private key type that jwt library can handle directly. | ||
| // For crypto.Signer implementations (KMS/HSM), use custom signing. | ||
| switch s.Key.(type) { | ||
| case *rsa.PrivateKey, *ecdsa.PrivateKey, ed25519.PrivateKey: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type system doesn't enforce that c.Key is non-nil. If there's a check somewhere that would catch that, that's fine, but it's probably best to return an error instead of allowing a potential nil dereference panic.
What really matters is whether your change would cause a downgrade. If previously the user would not encounter a panic when providing a nil key/signer and after your changes the user would encounter a panic, that is not okay. If, on the other hand, a user would have encountered a panic before your changes, it's acceptable for them to encounter a panic after your changes.
9ad3a32 to
9f655f0
Compare
Check public key type instead of private key type to support crypto.Signer implementations (GCP KMS, AWS KMS, HSM) that aren't concrete *rsa.PrivateKey or *ecdsa.PrivateKey types. Changes: - samlsp/new.go: Update defaultSigningMethodForKey() - samlsp/session_jwt.go: Add fallback signing with crypto.Signer - samlsp/request_tracker_jwt.go: Add fallback signing - service_provider.go: Update GetSigningContext() validation
9f655f0 to
9399db6
Compare
Summary
crypto.Signerimplementations (GCP KMS, AWS KMS, HSM) that aren't concrete*rsa.PrivateKeyor*ecdsa.PrivateKeytypescrypto.Signerinterface for KMS/HSM keysGetSigningContext()to check public key type