Skip to content

Add TPM support with crypto.Signer#32

Merged
leelynne merged 6 commits intomainfrom
leef-tpm-support
Aug 22, 2025
Merged

Add TPM support with crypto.Signer#32
leelynne merged 6 commits intomainfrom
leef-tpm-support

Conversation

@leelynne
Copy link
Copy Markdown
Collaborator

@leelynne leelynne commented Aug 16, 2025

This adds a Signer member of type crypto.Signer to the SigningKey struct in addition to a private key to support TPM backed signing.

SigningKey retains the PrivateKey member for usage ergonomics and to allow for type checking when signing algorithms.

@leelynne leelynne self-assigned this Aug 16, 2025
@leelynne
Copy link
Copy Markdown
Collaborator Author

@getvictor Here is a PR for supporting crypto.Signer. Let me know if this will work for your use cases or if it needs any changes

Comment thread signatures.go Outdated
}
} else {
// crypto.Signer for ECDSA returns the signature in ASN.1 format
asn1Sig, err := pkSigner.Sign(rand.Reader, msgHash[:], crypto.SHA256)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

In our case, TPM Sign does not return ASN.1 format, and we create the proper HTTP signature format ourselves: https://github.com/fleetdm/fleet/blob/34c45b256f54298aae0fc7bd70c68cd6a99faf7a/ee/orbit/pkg/securehw/securehw_tpm.go#L520

We don't want to asn1.Marshal the result just so it can be unmarshaled in this method.

Go’s encoding/asn1 works, but it’s reflective and allocation-heavy. Parsing/serializing with asn1.Unmarshal/asn1.Marshal is measurably slow.

We would like to return the HTTP signature directly from our Signer.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

That makes sense, which is why I kept the ecdsa.Sign method above.

However, the ecdsa.Sign method that implements crypto.Signer declares it returns ASN1 format - https://pkg.go.dev/crypto/ecdsa#PrivateKey.Sign

So if someone where to pass a crypto.Signer based on an ecdsa private key I have no way of knowing if the signing format is ASN1 or not.
Any suggestions for this? It feels like I have to respect the documentation here but I also don't want the ASN1 overhead. I could add member variable like isASN1 to the SigningKey struct but that feels ugly.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Another option would be, if crypto.Signer is provided in the SigningKey, the code would have to check if the implementation of crypto.Signer was a *ecdsa.PrivateKey. If not *ecdsa.PrivateKey then assume the signature is not ASN.1 encoded.

I am not a big fan of that either. It feels better to be explicit.

Comment thread signatures.go
Comment thread signatures.go
Comment thread keyutil/keyutil.go Outdated
leelynne and others added 3 commits August 18, 2025 16:54
Co-authored-by: Harshita Chaudhary <hchaudhary2511@gmail.com>
…her to expect an ASN1 encoded signature for ECDSA signatures.
@leelynne
Copy link
Copy Markdown
Collaborator Author

@getvictor I redid the interface slightly to indicate whether the crypto.Signer returns ASN1 formatted signatures (default is no) and added test cases. Let me know what you think. If you have better ideas for the user interface (SigningKey, SigningKeyOpts) let me know.

Copy link
Copy Markdown
Contributor

@getvictor getvictor left a comment

Choose a reason for hiding this comment

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

LGTM

@leelynne leelynne merged commit 1bd7707 into main Aug 22, 2025
2 checks passed
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.

3 participants