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

Windows hacking #2

Draft
wants to merge 27 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
3e6fc9e
Add TPM 2 application key support for Windows
mjg59 Apr 28, 2022
ade05c9
x509ext: initial version of package
brandonweeks Apr 8, 2022
164122a
Add QualifyingData paramater to KeyConfig
brandonweeks Jun 1, 2022
ab74553
Merge remote-tracking branch 'bweeks/x509ext' into patches
hslatman Oct 27, 2022
31a9234
Merge remote-tracking branch 'mjg59/windows_application_keys' into pa…
hslatman Oct 27, 2022
f548032
Merge remote-tracking branch 'bweeks/acme-device-attest' into patches
hslatman Oct 27, 2022
81aa7c3
Fix `QualifyingData` on Windows
hslatman Nov 8, 2022
1a8e4e7
Add signing support for keys generated on Windows
hslatman Nov 10, 2022
5bc739d
Implement `blobs` for Windows keys
hslatman Nov 14, 2022
eb68d97
Add `loadKey` on Windows
hslatman Nov 14, 2022
3543ffd
Remove superfluous return
hslatman Nov 14, 2022
3737d78
Add `Name` and `Prefix` options to `KeyConfig`
hslatman Nov 15, 2022
4dd9dc6
Disable Windows TPM check for `KeyConfig` properties
hslatman Nov 15, 2022
b832351
Add `Name` and `Prefix` to AK creation
hslatman Jan 4, 2023
7ad3b26
Merge branch 'master' into herman/windows-hacking
hslatman Jan 4, 2023
7d9b67d
Fix missing `ECDSA` curve when signing on Windows
hslatman Jan 4, 2023
d197d79
Add support for deleting keys
hslatman Jan 5, 2023
37fd3fa
Fix tests for TPM 1.2
hslatman Jan 13, 2023
a3f530a
Merge branch 'master' into herman/windows-hacking
hslatman Jan 13, 2023
0ad94dd
Fix tests for TPM 1.2
hslatman Jan 13, 2023
0ea71a1
Add `DeleteAK` method
hslatman Feb 24, 2023
eb81e6e
Add `Blobs` method for `AK`
hslatman Feb 24, 2023
1bcb20a
Add missing methods for TPM 1.2
hslatman Feb 24, 2023
2f91901
Merge branch 'master' into herman/windows-hacking
hslatman May 8, 2023
ef181aa
Remove key prefix and some cleanup
hslatman May 9, 2023
8df7c54
Merge branch 'master' into herman/windows-hacking
hslatman Jun 27, 2023
ce1f4b5
Fix legacy TPM2 path for Windows key creation and loading
hslatman Jun 27, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions attest/application_key.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,13 @@ type KeyConfig struct {
// Size is used to specify the bit size of the key or elliptic curve. For
// example, '256' is used to specify curve P-256.
Size int
// QualifyingData is data provided from outside to the TPM when an attestation
// operation is performed. The TPM doesn't interpret the data, but does sign over
// it. It can be used as a nonce to ensure freshness of an attestation.
QualifyingData []byte
// Name is used to specify a name for the key, instead of generating
// a random one. This property is only used on Windows.
Name string
}

// defaultConfig is used when no other configuration is specified.
Expand Down
14 changes: 12 additions & 2 deletions attest/attest.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,8 @@ type ak interface {
activateCredential(tpm tpmBase, in EncryptedCredential, ek *EK) ([]byte, error)
quote(t tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error)
attestationParameters() AttestationParameters
certify(tb tpmBase, handle interface{}) (*CertificationParameters, error)
certify(tb tpmBase, handle interface{}, qualifyingData []byte) (*CertificationParameters, error)
blobs() ([]byte, []byte, error)
}

// AK represents a key which can be used for attestation.
Expand Down Expand Up @@ -166,11 +167,20 @@ func (k *AK) AttestationParameters() AttestationParameters {
// key. Depending on the actual instantiation it can accept different handle
// types (e.g., tpmutil.Handle on Linux or uintptr on Windows).
func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, error) {
return k.ak.certify(tpm.tpm, handle)
return k.ak.certify(tpm.tpm, handle, nil)
}

// Blobs returns public and private blobs to be used by tpm2.Load().
func (k *AK) Blobs() (pub, priv []byte, err error) {
return k.ak.blobs()
}

// AKConfig encapsulates parameters for minting keys.
type AKConfig struct {
// Name is used to specify a name for the key, instead of generating
// a random one. This property is only used on Windows.
Name string

// The EK that will be used for attestation.
// If nil, an RSA EK with handle 0x81010001 will be used.
// If not nil, it must be one of EKs returned from TPM.EKs().
Expand Down
4 changes: 2 additions & 2 deletions attest/certification.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ func (p *CertificationParameters) Generate(rnd io.Reader, verifyOpts VerifyOpts,

// certify uses AK's handle and the passed signature scheme to certify the key
// with the `hnd` handle.
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigScheme) (*CertificationParameters, error) {
func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, qualifyingData []byte, scheme tpm2.SigScheme) (*CertificationParameters, error) {
pub, _, _, err := tpm2.ReadPublic(tpm, hnd)
if err != nil {
return nil, fmt.Errorf("tpm2.ReadPublic() failed: %v", err)
Expand All @@ -247,7 +247,7 @@ func certify(tpm io.ReadWriteCloser, hnd, akHnd tpmutil.Handle, scheme tpm2.SigS
if err != nil {
return nil, fmt.Errorf("could not encode public key: %v", err)
}
att, sig, err := tpm2.CertifyEx(tpm, "", "", hnd, akHnd, nil, scheme)
att, sig, err := tpm2.CertifyEx(tpm, "", "", hnd, akHnd, qualifyingData, scheme)
if err != nil {
return nil, fmt.Errorf("tpm2.Certify() failed: %v", err)
}
Expand Down
7 changes: 6 additions & 1 deletion attest/key_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package attest

import (
"errors"
"fmt"

"github.com/google/go-tspi/attestation"
Expand Down Expand Up @@ -96,6 +97,10 @@ func (k *trousersKey12) attestationParameters() AttestationParameters {
}
}

func (k *trousersKey12) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
func (k *trousersKey12) certify(tb tpmBase, handle interface{}, qualifyingData []byte) (*CertificationParameters, error) {
return nil, fmt.Errorf("not implemented")
}

func (k *trousersKey12) blobs() ([]byte, []byte, error) {
return nil, nil, errors.New("not implemented")
}
108 changes: 87 additions & 21 deletions attest/key_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,28 +18,32 @@
package attest

import (
"crypto"
"crypto/ecdsa"
"crypto/rsa"
"errors"
"fmt"

"github.com/google/go-tpm/legacy/tpm2"
tpm1 "github.com/google/go-tpm/tpm"
)

// windowsKey12 represents a Windows-managed key on a TPM1.2 TPM.
type windowsKey12 struct {
// windowsAK12 represents a Windows-managed key on a TPM1.2 TPM.
type windowsAK12 struct {
hnd uintptr
pcpKeyName string
public []byte
}

func newWindowsKey12(hnd uintptr, pcpKeyName string, public []byte) ak {
return &windowsKey12{
func newWindowsAK12(hnd uintptr, pcpKeyName string, public []byte) ak {
return &windowsAK12{
hnd: hnd,
pcpKeyName: pcpKeyName,
public: public,
}
}

func (k *windowsKey12) marshal() ([]byte, error) {
func (k *windowsAK12) marshal() ([]byte, error) {
out := serializedKey{
Encoding: keyEncodingOSManaged,
TPMVersion: TPMVersion12,
Expand All @@ -49,7 +53,7 @@ func (k *windowsKey12) marshal() ([]byte, error) {
return out.Serialize()
}

func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
func (k *windowsAK12) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
tpm, ok := t.(*windowsTPM)
if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
Expand All @@ -61,7 +65,7 @@ func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential, ek
return decryptCredential(secretKey, in.Secret)
}

func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
func (k *windowsAK12) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
if alg != HashSHA1 {
return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg)
}
Expand Down Expand Up @@ -98,21 +102,25 @@ func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs
}, nil
}

func (k *windowsKey12) close(tpm tpmBase) error {
func (k *windowsAK12) close(tpm tpmBase) error {
return closeNCryptObject(k.hnd)
}

func (k *windowsKey12) attestationParameters() AttestationParameters {
func (k *windowsAK12) attestationParameters() AttestationParameters {
return AttestationParameters{
Public: k.public,
}
}
func (k *windowsKey12) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
func (k *windowsAK12) certify(tb tpmBase, handle interface{}, qualifyingData []byte) (*CertificationParameters, error) {
return nil, fmt.Errorf("not implemented")
}

// windowsKey20 represents a key bound to a TPM 2.0.
type windowsKey20 struct {
func (k *windowsAK12) blobs() ([]byte, []byte, error) {
return nil, nil, errors.New("not implemented")
}

// windowsAK20 represents a key bound to a TPM 2.0.
type windowsAK20 struct {
hnd uintptr

pcpKeyName string
Expand All @@ -122,8 +130,8 @@ type windowsKey20 struct {
createSignature []byte
}

func newWindowsKey20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) ak {
return &windowsKey20{
func newWindowsAK20(hnd uintptr, pcpKeyName string, public, createData, createAttest, createSig []byte) ak {
return &windowsAK20{
hnd: hnd,
pcpKeyName: pcpKeyName,
public: public,
Expand All @@ -133,7 +141,7 @@ func newWindowsKey20(hnd uintptr, pcpKeyName string, public, createData, createA
}
}

func (k *windowsKey20) marshal() ([]byte, error) {
func (k *windowsAK20) marshal() ([]byte, error) {
out := serializedKey{
Encoding: keyEncodingOSManaged,
TPMVersion: TPMVersion20,
Expand All @@ -147,15 +155,15 @@ func (k *windowsKey20) marshal() ([]byte, error) {
return out.Serialize()
}

func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
func (k *windowsAK20) activateCredential(t tpmBase, in EncryptedCredential, ek *EK) ([]byte, error) {
tpm, ok := t.(*windowsTPM)
if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", t)
}
return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...))
}

func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
func (k *windowsAK20) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs []int) (*Quote, error) {
t, ok := tb.(*windowsTPM)
if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
Expand All @@ -172,11 +180,11 @@ func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg, selectedPCRs
return quote20(tpm, tpmKeyHnd, alg.goTPMAlg(), nonce, selectedPCRs)
}

func (k *windowsKey20) close(tpm tpmBase) error {
func (k *windowsAK20) close(tpm tpmBase) error {
return closeNCryptObject(k.hnd)
}

func (k *windowsKey20) attestationParameters() AttestationParameters {
func (k *windowsAK20) attestationParameters() AttestationParameters {
return AttestationParameters{
Public: k.public,
CreateData: k.createData,
Expand All @@ -185,7 +193,7 @@ func (k *windowsKey20) attestationParameters() AttestationParameters {
}
}

func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) {
func (k *windowsAK20) certify(tb tpmBase, handle interface{}, qualifyingData []byte) (*CertificationParameters, error) {
t, ok := tb.(*windowsTPM)
if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
Expand All @@ -210,5 +218,63 @@ func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa
Alg: tpm2.AlgRSASSA,
Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1
}
return certify(tpm, hnd, akHnd, scheme)
return certify(tpm, hnd, akHnd, qualifyingData, scheme)
}

// newWindowsKey20 returns a pointer to a windowsAK20, conforming to the key interface. This
// allows the resulting windowsAK20 to be used as a signing key.
func newWindowsKey20(hnd uintptr, pcpKeyName string, pub, createData, createAttest, createSig []byte) key {
return &windowsAK20{
hnd: hnd,
pcpKeyName: pcpKeyName,
public: pub,
createData: createData,
createAttestation: createAttest,
createSignature: createSig,
}
}

func (k *windowsAK20) blobs() ([]byte, []byte, error) {
// TODO(hslatman): check if this is required on Windows? `newKey` seems to create
// persistent keys with a name, so it may be possible to load the key by name instead?
return nil, nil, errors.New("not implemented")
}

func (k *windowsAK20) certificationParameters() CertificationParameters {
return CertificationParameters{
Public: k.public,
CreateAttestation: k.createAttestation,
CreateSignature: k.createSignature,
}
}

func (k *windowsAK20) decrypt(tpmBase, []byte) ([]byte, error) {
return nil, errors.New("not implemented")
}

func (k *windowsAK20) sign(tb tpmBase, digest []byte, pub crypto.PublicKey, opts crypto.SignerOpts) ([]byte, error) {

t, ok := tb.(*windowsTPM)
if !ok {
return nil, fmt.Errorf("expected *windowsTPM, got %T", tb)
}

rw, err := t.pcp.TPMCommandInterface()
if err != nil {
return nil, fmt.Errorf("error getting TPM command interface: %w", err)
}

hnd, err := t.pcp.TPMKeyHandle(k.hnd)
if err != nil {
return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err)
}

switch p := pub.(type) {
case *ecdsa.PublicKey:
return signECDSA(rw, hnd, digest, p.Curve)
case *rsa.PublicKey:
return signRSA(rw, hnd, digest, opts)
}

return nil, fmt.Errorf("unsupported signing key type: %T", pub)
}
Loading