From 3e6fc9e34b97eb48382015b6a63871001e955d6e Mon Sep 17 00:00:00 2001 From: Matthew Garrett <mgarrett@aurora.tech> Date: Thu, 28 Apr 2022 16:27:01 -0700 Subject: [PATCH 01/20] Add TPM 2 application key support for Windows There's currently no support for creating application keys on Windows systems. This patch transitions the Windows key type to specifically refer to attestation keys, and reuses the existing wrapped key support for application keys. This allows the creation of keys in the platform store, while still allowing said keys to be manipulated with existing TPM functionality rather than duplicating it. --- attest/key_windows.go | 40 ++++---- attest/pcp_windows.go | 212 ++++++++++++++++++++++++++++++++++++------ attest/tpm_windows.go | 52 +++++++++-- 3 files changed, 251 insertions(+), 53 deletions(-) diff --git a/attest/key_windows.go b/attest/key_windows.go index 9d153c71..69e8ce0b 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -24,22 +24,22 @@ import ( "github.com/google/go-tpm/tpm2" ) -// 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, @@ -49,7 +49,7 @@ func (k *windowsKey12) marshal() ([]byte, error) { return out.Serialize() } -func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) { +func (k *windowsAK12) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) { tpm, ok := t.(*windowsTPM) if !ok { return nil, fmt.Errorf("expected *windowsTPM, got %T", t) @@ -61,7 +61,7 @@ func (k *windowsKey12) activateCredential(t tpmBase, in EncryptedCredential) ([] return decryptCredential(secretKey, in.Secret) } -func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) { +func (k *windowsAK12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) { if alg != HashSHA1 { return nil, fmt.Errorf("only SHA1 algorithms supported on TPM 1.2, not %v", alg) } @@ -103,21 +103,21 @@ func (k *windowsKey12) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, err }, 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{}) (*CertificationParameters, error) { return nil, fmt.Errorf("not implemented") } -// windowsKey20 represents a key bound to a TPM 2.0. -type windowsKey20 struct { +// windowsAK20 represents a key bound to a TPM 2.0. +type windowsAK20 struct { hnd uintptr pcpKeyName string @@ -127,8 +127,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, @@ -138,7 +138,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, @@ -152,7 +152,7 @@ func (k *windowsKey20) marshal() ([]byte, error) { return out.Serialize() } -func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) { +func (k *windowsAK20) activateCredential(t tpmBase, in EncryptedCredential) ([]byte, error) { tpm, ok := t.(*windowsTPM) if !ok { return nil, fmt.Errorf("expected *windowsTPM, got %T", t) @@ -160,7 +160,7 @@ func (k *windowsKey20) activateCredential(t tpmBase, in EncryptedCredential) ([] return tpm.pcp.ActivateCredential(k.hnd, append(in.Credential, in.Secret...)) } -func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) { +func (k *windowsAK20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) { t, ok := tb.(*windowsTPM) if !ok { return nil, fmt.Errorf("expected *windowsTPM, got %T", tb) @@ -177,11 +177,11 @@ func (k *windowsKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, err return quote20(tpm, tpmKeyHnd, alg.goTPMAlg(), nonce) } -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, @@ -190,7 +190,7 @@ func (k *windowsKey20) attestationParameters() AttestationParameters { } } -func (k *windowsKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { +func (k *windowsAK20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { t, ok := tb.(*windowsTPM) if !ok { return nil, fmt.Errorf("expected *windowsTPM, got %T", tb) diff --git a/attest/pcp_windows.go b/attest/pcp_windows.go index a139ec6f..88a9b699 100644 --- a/attest/pcp_windows.go +++ b/attest/pcp_windows.go @@ -38,8 +38,17 @@ const ( // The below is documented in this Microsoft whitepaper: // https://github.com/Microsoft/TSS.MSR/blob/master/PCPTool.v11/Using%20the%20Windows%208%20Platform%20Crypto%20Provider%20and%20Associated%20TPM%20Functionality.pdf ncryptOverwriteKeyFlag = 0x80 + // Key usage value for generic keys + nCryptPropertyPCPKeyUsagePolicyGeneric = 0x3 // Key usage value for AKs. nCryptPropertyPCPKeyUsagePolicyIdentity = 0x8 + + // PCP key magic + pcpKeyMagic = 0x4D504350 + + // TPM types from PCP_KEY_BLOB header data + tpm12 = 0x1 + tpm20 = 0x2 ) // DLL references. @@ -53,6 +62,7 @@ var ( nCryptCreatePersistedKey = nCrypt.MustFindProc("NCryptCreatePersistedKey") nCryptFinalizeKey = nCrypt.MustFindProc("NCryptFinalizeKey") nCryptDeleteKey = nCrypt.MustFindProc("NCryptDeleteKey") + nCryptExportKey = nCrypt.MustFindProc("NCryptExportKey") crypt32 = windows.MustLoadDLL("crypt32.dll") crypt32CertEnumCertificatesInStore = crypt32.MustFindProc("CertEnumCertificatesInStore") @@ -452,16 +462,15 @@ func getPCPCerts(hProv uintptr, propertyName string) ([][]byte, error) { return out, nil } -// NewAK creates a persistent attestation key of the specified name. -func (h *winPCP) NewAK(name string) (uintptr, error) { +func (h *winPCP) newKey(name string, alg string, length uint32, policy uint32) (uintptr, []byte, []byte, error) { var kh uintptr utf16Name, err := windows.UTF16FromString(name) if err != nil { - return 0, err + return 0, nil, nil, err } - utf16RSA, err := windows.UTF16FromString("RSA") + utf16RSA, err := windows.UTF16FromString(alg) if err != nil { - return 0, err + return 0, nil, nil, err } // Create a persistent RSA key of the specified name. @@ -470,45 +479,102 @@ func (h *winPCP) NewAK(name string) (uintptr, error) { if tpmErr := maybeWinErr(r); tpmErr != nil { msg = tpmErr } - return 0, fmt.Errorf("NCryptCreatePersistedKey returned %X: %v", r, msg) + return 0, nil, nil, fmt.Errorf("NCryptCreatePersistedKey returned %X: %v", r, msg) } - // Specify generated key length to be 2048 bits. - utf16Length, err := windows.UTF16FromString("Length") - if err != nil { - return 0, err + + // Set the length if provided + if length != 0 { + utf16Length, err := windows.UTF16FromString("Length") + if err != nil { + return 0, nil, nil, err + } + r, _, msg = nCryptSetProperty.Call(kh, uintptr(unsafe.Pointer(&utf16Length[0])), uintptr(unsafe.Pointer(&length)), unsafe.Sizeof(length), 0) + if r != 0 { + if tpmErr := maybeWinErr(r); tpmErr != nil { + msg = tpmErr + } + return 0, nil, nil, fmt.Errorf("NCryptSetProperty (Length) returned %X: %v", r, msg) + } + } + // Specify the generated key usage policy if appropriate + if policy != 0 { + utf16KeyPolicy, err := windows.UTF16FromString("PCP_KEY_USAGE_POLICY") + if err != nil { + return 0, nil, nil, err + } + r, _, msg = nCryptSetProperty.Call(kh, uintptr(unsafe.Pointer(&utf16KeyPolicy[0])), uintptr(unsafe.Pointer(&policy)), unsafe.Sizeof(policy), 0) + if r != 0 { + if tpmErr := maybeWinErr(r); tpmErr != nil { + msg = tpmErr + } + return 0, nil, nil, fmt.Errorf("NCryptSetProperty (PCP KeyUsage Policy) returned %X: %v", r, msg) + } } - var length uint32 = 2048 - r, _, msg = nCryptSetProperty.Call(kh, uintptr(unsafe.Pointer(&utf16Length[0])), uintptr(unsafe.Pointer(&length)), unsafe.Sizeof(length), 0) + + // Finalize (create) the key. + r, _, msg = nCryptFinalizeKey.Call(kh, 0) if r != 0 { if tpmErr := maybeWinErr(r); tpmErr != nil { msg = tpmErr } - return 0, fmt.Errorf("NCryptSetProperty (Length) returned %X: %v", r, msg) + return 0, nil, nil, fmt.Errorf("NCryptFinalizeKey returned %X: %v", r, msg) } - // Specify the generated key can only be used for identity attestation. - utf16KeyPolicy, err := windows.UTF16FromString("PCP_KEY_USAGE_POLICY") + + // Obtain the key blob. + var sz uint32 + typeString, err := windows.UTF16FromString("OpaqueKeyBlob") if err != nil { - return 0, err + return 0, nil, nil, err } - var policy uint32 = nCryptPropertyPCPKeyUsagePolicyIdentity - r, _, msg = nCryptSetProperty.Call(kh, uintptr(unsafe.Pointer(&utf16KeyPolicy[0])), uintptr(unsafe.Pointer(&policy)), unsafe.Sizeof(policy), 0) - if r != 0 { + + if r, _, err := nCryptExportKey.Call(kh, 0, uintptr(unsafe.Pointer(&typeString[0])), 0, 0, 0, uintptr(unsafe.Pointer(&sz)), 0); r != 0 { if tpmErr := maybeWinErr(r); tpmErr != nil { - msg = tpmErr + err = tpmErr } - return 0, fmt.Errorf("NCryptSetProperty (PCP KeyUsage Policy) returned %X: %v", r, msg) + return 0, nil, nil, fmt.Errorf("NCryptGetProperty for hKey blob original query returned %X (%v)", r, err) } - // Finalize (create) the key. - r, _, msg = nCryptFinalizeKey.Call(kh, 0) - if r != 0 { + keyBlob := make([]byte, sz) + + if r, _, err := nCryptExportKey.Call(kh, 0, uintptr(unsafe.Pointer(&typeString[0])), 0, uintptr(unsafe.Pointer(&keyBlob[0])), uintptr(sz), uintptr(unsafe.Pointer(&sz)), 0); r != 0 { if tpmErr := maybeWinErr(r); tpmErr != nil { - msg = tpmErr + err = tpmErr } - return 0, fmt.Errorf("NCryptFinalizeKey returned %X: %v", r, msg) + return 0, nil, nil, fmt.Errorf("NCryptGetProperty for hKey blob returned %X (%v)", r, err) + } + + pubBlob, privBlob, err := decodeKeyBlob(keyBlob) + if err != nil { + return 0, nil, nil, fmt.Errorf("decodeKeyBlob failed: %v", err) } - return kh, nil + return kh, pubBlob, privBlob, nil +} + +// NewAK creates a persistent attestation key of the specified name. +func (h *winPCP) NewAK(name string) (uintptr, error) { + // AKs need to be RSA due to platform limitations + key, _, _, err := h.newKey(name, "RSA", 2048, nCryptPropertyPCPKeyUsagePolicyIdentity) + return key, err +} + +// NewKey creates a persistent application key of the specified name. +func (h *winPCP) NewKey(name string, config *KeyConfig) (uintptr, []byte, []byte, error) { + if config.Algorithm == RSA { + return h.newKey(name, "RSA", uint32(config.Size), 0) + } else if config.Algorithm == ECDSA { + switch config.Size { + case 256: + return h.newKey(name, "ECDSA_P256", 0, 0) + case 384: + return h.newKey(name, "ECDSA_P384", 0, 0) + case 521: + return h.newKey(name, "ECDSA_P521", 0, 0) + default: + return 0, nil, nil, fmt.Errorf("unsupported ECDSA key size: %v", config.Size) + } + } + return 0, nil, nil, fmt.Errorf("unsupported algorithm type: %q", config.Algorithm) } // EKPub returns a BCRYPT_RSA_BLOB structure representing the EK. @@ -635,6 +701,98 @@ func decodeAKProps20(r *bytes.Reader) (*akProps, error) { return &out, nil } +func decodeKeyBlob(keyBlob []byte) ([]byte, []byte, error) { + r := bytes.NewReader(keyBlob) + + var magic uint32 + if err := binary.Read(r, binary.LittleEndian, &magic); err != nil { + return nil, nil, fmt.Errorf("failed to read header magic: %v", err) + } + if magic != pcpKeyMagic { + return nil, nil, fmt.Errorf("invalid header magic %X", magic) + } + + var headerSize uint32 + if err := binary.Read(r, binary.LittleEndian, &headerSize); err != nil { + return nil, nil, fmt.Errorf("failed to read header size: %v", err) + } + + var tpmType uint32 + if err := binary.Read(r, binary.LittleEndian, &tpmType); err != nil { + return nil, nil, fmt.Errorf("failed to read tpm type: %v", err) + } + + if tpmType == tpm12 { + return nil, nil, fmt.Errorf("TPM 1.2 currently unsupported") + } + + var flags uint32 + if err := binary.Read(r, binary.LittleEndian, &flags); err != nil { + return nil, nil, fmt.Errorf("failed to read key flags: %v", err) + } + + var pubLen uint32 + if err := binary.Read(r, binary.LittleEndian, &pubLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of public key: %v", err) + } + + var privLen uint32 + if err := binary.Read(r, binary.LittleEndian, &privLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of private blob: %v", err) + } + + var pubMigrationLen uint32 + if err := binary.Read(r, binary.LittleEndian, &pubMigrationLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of public migration blob: %v", err) + } + + var privMigrationLen uint32 + if err := binary.Read(r, binary.LittleEndian, &privMigrationLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of private migration blob: %v", err) + } + + var policyDigestLen uint32 + if err := binary.Read(r, binary.LittleEndian, &policyDigestLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of policy digest: %v", err) + } + + var pcrBindingLen uint32 + if err := binary.Read(r, binary.LittleEndian, &pcrBindingLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of PCR binding: %v", err) + } + + var pcrDigestLen uint32 + if err := binary.Read(r, binary.LittleEndian, &pcrDigestLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of PCR digest: %v", err) + } + + var encryptedSecretLen uint32 + if err := binary.Read(r, binary.LittleEndian, &encryptedSecretLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of hostage import symmetric key: %v", err) + } + + var tpm12HostageLen uint32 + if err := binary.Read(r, binary.LittleEndian, &tpm12HostageLen); err != nil { + return nil, nil, fmt.Errorf("failed to read length of hostage import private key: %v", err) + } + + // Skip over any padding + r.Seek(headerSize, 0) + + pubKey := make([]byte, pubLen) + + if err := binary.Read(r, binary.BigEndian, &pubKey); err != nil { + return nil, nil, fmt.Errorf("failed to read public key: %v", err) + } + + privBlob := make([]byte, privLen) + if err := binary.Read(r, binary.BigEndian, &privBlob); err != nil { + return nil, nil, fmt.Errorf("failed to read private blob: %v", err) + } + + return pubKey[2:], privBlob[2:], nil +} + // LoadKeyByName returns a handle to the persistent PCP key with the specified // name. func (h *winPCP) LoadKeyByName(name string) (uintptr, error) { diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 0203c767..f379cff1 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -30,6 +30,8 @@ import ( "math/big" tpm1 "github.com/google/go-tpm/tpm" + "github.com/google/go-tpm/tpm2" + "github.com/google/go-tpm/tpmutil" tpmtbs "github.com/google/go-tpm/tpmutil/tbs" "golang.org/x/sys/windows" ) @@ -293,9 +295,9 @@ func (t *windowsTPM) newAK(opts *AKConfig) (*AK, error) { switch t.version { case TPMVersion12: - return &AK{ak: newWindowsKey12(kh, name, props.RawPublic)}, nil + return &AK{ak: newWindowsAK12(kh, name, props.RawPublic)}, nil case TPMVersion20: - return &AK{ak: newWindowsKey20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature)}, nil + return &AK{ak: newWindowsAK20(kh, name, props.RawPublic, props.RawCreationData, props.RawAttest, props.RawSignature)}, nil default: return nil, fmt.Errorf("cannot handle TPM version: %v", t.version) } @@ -317,16 +319,54 @@ func (t *windowsTPM) loadAK(opaqueBlob []byte) (*AK, error) { switch t.version { case TPMVersion12: - return &AK{ak: newWindowsKey12(hnd, sKey.Name, sKey.Public)}, nil + return &AK{ak: newWindowsAK12(hnd, sKey.Name, sKey.Public)}, nil case TPMVersion20: - return &AK{ak: newWindowsKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil + return &AK{ak: newWindowsAK20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil default: return nil, fmt.Errorf("cannot handle TPM version: %v", t.version) } } -func (t *windowsTPM) newKey(*AK, *KeyConfig) (*Key, error) { - return nil, fmt.Errorf("not implemented") +func (t *windowsTPM) newKey(ak *AK, config *KeyConfig) (*Key, error) { + if t.version != TPMVersion20 { + return nil, fmt.Errorf("key generation on TPM version %v is unsupported", t.version) + } + k, ok := ak.ak.(*windowsAK20) + if !ok { + return nil, fmt.Errorf("expected *windowsAK20, got: %T", k) + } + + nameHex := make([]byte, 5) + if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) { + return nil, fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err) + } + name := fmt.Sprintf("app-%x", nameHex) + + hnd, pub, blob, err := t.pcp.NewKey(name, config) + if err != nil { + return nil, fmt.Errorf("pcp failed to mint application key: %v", err) + } + + cp, err := k.certify(t, hnd) + if err != nil { + return nil, fmt.Errorf("ak.Certify() failed: %v", err) + } + + if !bytes.Equal(pub, cp.Public) { + return nil, fmt.Errorf("certified incorrect key, expected: %v, certified: %v", pub, cp.Public) + } + + tpmPub, err := tpm2.DecodePublic(pub) + if err != nil { + return nil, fmt.Errorf("decode public key: %v", err) + } + + pubKey, err := tpmPub.Key() + if err != nil { + return nil, fmt.Errorf("access public key: %v", err) + } + + return &Key{key: newWrappedKey20(tpmutil.Handle(hnd), blob, pub, nil, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil } func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) { From ade05c952a6547862ec51d014e98479476ae594b Mon Sep 17 00:00:00 2001 From: Brandon Weeks <bweeks@google.com> Date: Fri, 8 Apr 2022 11:25:35 -0700 Subject: [PATCH 02/20] x509ext: initial version of package --- x509/x509ext.go | 174 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 174 insertions(+) create mode 100644 x509/x509ext.go diff --git a/x509/x509ext.go b/x509/x509ext.go new file mode 100644 index 00000000..0a8f8bf1 --- /dev/null +++ b/x509/x509ext.go @@ -0,0 +1,174 @@ +// Package x509ext provides functions for (un)marshalling X.509 extensions not +// supported by the crypto/x509 package. +package x509ext + +import ( + "crypto/x509/pkix" + "encoding/asn1" + "errors" + "fmt" + + "github.com/google/go-attestation/oid" +) + +// RFC 4043 +// +// https://tools.ietf.org/html/rfc4043 +var ( + oidPermanentIdentifier = []int{1, 3, 6, 1, 5, 5, 7, 8, 3} +) + +// OtherName ::= SEQUENCE { +// type-id OBJECT IDENTIFIER, +// value [0] EXPLICIT ANY DEFINED BY type-id } +type otherName struct { + TypeID asn1.ObjectIdentifier + Value asn1.RawValue +} + +func marshalOtherName(typeID asn1.ObjectIdentifier, value interface{}) (asn1.RawValue, error) { + valueBytes, err := asn1.MarshalWithParams(value, "explicit,tag:0") + if err != nil { + return asn1.RawValue{}, err + } + otherName := otherName{ + TypeID: typeID, + Value: asn1.RawValue{FullBytes: valueBytes}, + } + bytes, err := asn1.MarshalWithParams(otherName, "tag:0") + if err != nil { + return asn1.RawValue{}, err + } + return asn1.RawValue{FullBytes: bytes}, nil +} + +// PermanentIdentifier ::= SEQUENCE { +// identifierValue UTF8String OPTIONAL, +// assigner OBJECT IDENTIFIER OPTIONAL +// } +// +// https://datatracker.ietf.org/doc/html/rfc4043 +type PermanentIdentifier struct { + IdentifierValue string `asn1:"utf8,optional"` + Assigner asn1.ObjectIdentifier `asn1:"optional"` +} + +func parsePermanentIdentifier(der []byte) (PermanentIdentifier, error) { + var permID PermanentIdentifier + if _, err := asn1.UnmarshalWithParams(der, &permID, "explicit,tag:0"); err != nil { + return PermanentIdentifier{}, err + } + return permID, nil +} + +// SubjectAltName contains GeneralName variations not supported by the +// crypto/x509 package. +// +// https://datatracker.ietf.org/doc/html/rfc5280 +type SubjectAltName struct { + DirectoryNames []pkix.Name + PermanentIdentifiers []PermanentIdentifier +} + +// ParseSubjectAltName parses a pkix.Extension into a SubjectAltName struct. +func ParseSubjectAltName(ext pkix.Extension) (*SubjectAltName, error) { + var out SubjectAltName + dirNames, otherNames, err := parseSubjectAltName(ext) + if err != nil { + return nil, fmt.Errorf("parseSubjectAltName: %v", err) + } + out.DirectoryNames = dirNames + + for _, otherName := range otherNames { + if otherName.TypeID.Equal(oidPermanentIdentifier) { + permID, err := parsePermanentIdentifier(otherName.Value.FullBytes) + if err != nil { + return nil, fmt.Errorf("parsePermanentIdentifier: %v", err) + } + out.PermanentIdentifiers = append(out.PermanentIdentifiers, permID) + } + } + return &out, nil +} + +// https://datatracker.ietf.org/doc/html/rfc5280#page-35 +func parseSubjectAltName(ext pkix.Extension) (dirNames []pkix.Name, otherNames []otherName, err error) { + err = forEachSAN(ext.Value, func(generalName asn1.RawValue) error { + switch generalName.Tag { + case 0: // otherName + var otherName otherName + if _, err := asn1.UnmarshalWithParams(generalName.FullBytes, &otherName, "tag:0"); err != nil { + return fmt.Errorf("OtherName: asn1.UnmarshalWithParams: %v", err) + } + otherNames = append(otherNames, otherName) + case 4: // directoryName + var rdns pkix.RDNSequence + if _, err := asn1.Unmarshal(generalName.Bytes, &rdns); err != nil { + return fmt.Errorf("DirectoryName: asn1.Unmarshal: %v", err) + } + var dirName pkix.Name + dirName.FillFromRDNSequence(&rdns) + dirNames = append(dirNames, dirName) + default: + return fmt.Errorf("expected tag %d", generalName.Tag) + } + return nil + }) + return +} + +// Borrowed from the x509 package. +func forEachSAN(extension []byte, callback func(ext asn1.RawValue) error) error { + var seq asn1.RawValue + rest, err := asn1.Unmarshal(extension, &seq) + if err != nil { + return err + } else if len(rest) != 0 { + return errors.New("x509: trailing data after X.509 extension") + } + if !seq.IsCompound || seq.Tag != 16 || seq.Class != 0 { + return asn1.StructuralError{Msg: "bad SAN sequence"} + } + + rest = seq.Bytes + for len(rest) > 0 { + var v asn1.RawValue + rest, err = asn1.Unmarshal(rest, &v) + if err != nil { + return err + } + + if err := callback(v); err != nil { + return err + } + } + + return nil +} + +// MarshalSubjectAltName converts a SubjectAltName struct into a pkix.Extension. +func MarshalSubjectAltName(san *SubjectAltName) (pkix.Extension, error) { + var generalNames []asn1.RawValue + for _, permID := range san.PermanentIdentifiers { + val, err := marshalOtherName(oidPermanentIdentifier, permID) + if err != nil { + return pkix.Extension{}, err + } + generalNames = append(generalNames, val) + } + for _, dirName := range san.DirectoryNames { + bytes, err := asn1.MarshalWithParams(dirName.ToRDNSequence(), "explicit,tag:4") + if err != nil { + return pkix.Extension{}, err + } + generalNames = append(generalNames, asn1.RawValue{FullBytes: bytes}) + } + val, err := asn1.Marshal(generalNames) + if err != nil { + return pkix.Extension{}, err + } + return pkix.Extension{ + Id: oid.SubjectAltName, + Value: val, + }, nil +} From 164122a1d59ba98c7c3eb591ab3d5e6c3d0071ac Mon Sep 17 00:00:00 2001 From: Brandon Weeks <bweeks@google.com> Date: Tue, 31 May 2022 17:15:41 -0700 Subject: [PATCH 03/20] Add QualifyingData paramater to KeyConfig --- attest/application_key.go | 2 ++ attest/attest.go | 4 ++-- attest/certification.go | 4 ++-- attest/wrapped_tpm20.go | 6 +++--- 4 files changed, 9 insertions(+), 7 deletions(-) diff --git a/attest/application_key.go b/attest/application_key.go index 2ee710d0..c22192be 100644 --- a/attest/application_key.go +++ b/attest/application_key.go @@ -72,6 +72,8 @@ 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 []byte } // defaultConfig is used when no other configuration is specified. diff --git a/attest/attest.go b/attest/attest.go index 226a4bd7..921fb117 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -104,7 +104,7 @@ type ak interface { activateCredential(tpm tpmBase, in EncryptedCredential) ([]byte, error) quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error) attestationParameters() AttestationParameters - certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) + certify(tb tpmBase, handle interface{}, qualifyingData []byte) (*CertificationParameters, error) } // AK represents a key which can be used for attestation. @@ -152,7 +152,7 @@ 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) } // AKConfig encapsulates parameters for minting keys. This type is defined diff --git a/attest/certification.go b/attest/certification.go index 372da046..2e85aca0 100644 --- a/attest/certification.go +++ b/attest/certification.go @@ -159,7 +159,7 @@ func (p *CertificationParameters) Verify(opts VerifyOpts) error { // 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) @@ -168,7 +168,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) } diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index 02db6872..3445201b 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -205,7 +205,7 @@ func (t *wrappedTPM20) newKey(ak *AK, opts *KeyConfig) (*Key, error) { }() // Certify application key by AK - cp, err := k.certify(t, keyHandle) + cp, err := k.certify(t, keyHandle, opts.QualifyingData) if err != nil { return nil, fmt.Errorf("ak.Certify() failed: %v", err) } @@ -455,7 +455,7 @@ func (k *wrappedKey20) activateCredential(tb tpmBase, in EncryptedCredential) ([ }, k.hnd, ekHnd, credential, secret) } -func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationParameters, error) { +func (k *wrappedKey20) certify(tb tpmBase, handle interface{}, qualifyingData []byte) (*CertificationParameters, error) { t, ok := tb.(*wrappedTPM20) if !ok { return nil, fmt.Errorf("expected *wrappedTPM20, got %T", tb) @@ -468,7 +468,7 @@ func (k *wrappedKey20) certify(tb tpmBase, handle interface{}) (*CertificationPa Alg: tpm2.AlgRSASSA, Hash: tpm2.AlgSHA256, } - return certify(t.rwc, hnd, k.hnd, scheme) + return certify(t.rwc, hnd, k.hnd, qualifyingData, scheme) } func (k *wrappedKey20) quote(tb tpmBase, nonce []byte, alg HashAlg) (*Quote, error) { From 81aa7c34859fcbf771cb9bbdffd0c797d4084ae5 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Tue, 8 Nov 2022 22:49:21 +0100 Subject: [PATCH 04/20] Fix `QualifyingData` on Windows --- attest/key_windows.go | 6 +++--- attest/pcp_windows.go | 5 +++-- attest/tpm_windows.go | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/attest/key_windows.go b/attest/key_windows.go index 69e8ce0b..9190a014 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -112,7 +112,7 @@ func (k *windowsAK12) attestationParameters() AttestationParameters { Public: k.public, } } -func (k *windowsAK12) 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") } @@ -190,7 +190,7 @@ func (k *windowsAK20) attestationParameters() AttestationParameters { } } -func (k *windowsAK20) 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) @@ -215,5 +215,5 @@ func (k *windowsAK20) certify(tb tpmBase, handle interface{}) (*CertificationPar Alg: tpm2.AlgRSASSA, Hash: tpm2.AlgSHA1, // PCP-created AK uses SHA1 } - return certify(tpm, hnd, akHnd, scheme) + return certify(tpm, hnd, akHnd, qualifyingData, scheme) } diff --git a/attest/pcp_windows.go b/attest/pcp_windows.go index 88a9b699..b01d5080 100644 --- a/attest/pcp_windows.go +++ b/attest/pcp_windows.go @@ -380,7 +380,8 @@ func (h *winPCP) Close() error { } // DeleteKey permanently removes the key with the given handle -// from the system, and frees its handle. +// +// from the system, and frees its handle. func (h *winPCP) DeleteKey(kh uintptr) error { r, _, msg := nCryptDeleteKey.Call(kh, 0) if r != 0 { @@ -777,7 +778,7 @@ func decodeKeyBlob(keyBlob []byte) ([]byte, []byte, error) { } // Skip over any padding - r.Seek(headerSize, 0) + r.Seek(int64(headerSize), 0) pubKey := make([]byte, pubLen) diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index f379cff1..e9fb870e 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -347,7 +347,7 @@ func (t *windowsTPM) newKey(ak *AK, config *KeyConfig) (*Key, error) { return nil, fmt.Errorf("pcp failed to mint application key: %v", err) } - cp, err := k.certify(t, hnd) + cp, err := k.certify(t, hnd, config.QualifyingData) if err != nil { return nil, fmt.Errorf("ak.Certify() failed: %v", err) } From 1a8e4e732040687f9747d1947ade967992b01a31 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Thu, 10 Nov 2022 15:08:08 +0100 Subject: [PATCH 05/20] Add signing support for keys generated on Windows When generating a new key using a Windows TPM, a `wrappedKey20` was returned, which couldn't be used for signing on Windows, as it's backed by a `windowsTPM`. The `wrappedKey20` seems to be a type specifically aimed at usage with a `wrappedTPM20`, which in turn seems to be used on Linux and for testing, but not when instantiating a TPM on Windows. This commit adds the `newWindowsKey20` function, which returns a key backed by a `windowsTPM`. The key is a `windowsAK20`, now also conforming to the `key` interface, so that it can be used for signing purposes. --- attest/key_windows.go | 60 +++++++++++++++++++++++++++++++++++++++++++ attest/tpm_windows.go | 9 +++++-- 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/attest/key_windows.go b/attest/key_windows.go index 9190a014..d431ad17 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -18,6 +18,10 @@ package attest import ( + "crypto" + "crypto/ecdsa" + "crypto/rsa" + "errors" "fmt" tpm1 "github.com/google/go-tpm/tpm" @@ -217,3 +221,59 @@ func (k *windowsAK20) certify(tb tpmBase, handle interface{}, qualifyingData []b } 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, public, createData, createAttest, createSig []byte) key { + return &windowsAK20{ + hnd: hnd, + pcpKeyName: pcpKeyName, + public: public, + createData: createData, + createAttestation: createAttest, + createSignature: createSig, + } +} + +func (k *windowsAK20) blobs() ([]byte, []byte, error) { + 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 pub.(type) { + case *ecdsa.PublicKey: + return signECDSA(rw, hnd, digest) + case *rsa.PublicKey: + return signRSA(rw, hnd, digest, opts) + } + + return nil, fmt.Errorf("unsupported signing key type: %T", pub) +} diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index e9fb870e..840aa9bd 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -31,7 +31,6 @@ import ( tpm1 "github.com/google/go-tpm/tpm" "github.com/google/go-tpm/tpm2" - "github.com/google/go-tpm/tpmutil" tpmtbs "github.com/google/go-tpm/tpmutil/tbs" "golang.org/x/sys/windows" ) @@ -366,7 +365,13 @@ func (t *windowsTPM) newKey(ak *AK, config *KeyConfig) (*Key, error) { return nil, fmt.Errorf("access public key: %v", err) } - return &Key{key: newWrappedKey20(tpmutil.Handle(hnd), blob, pub, nil, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil + // TODO(hslatman): do we need the blob? + _ = blob + + // Return a new windowsAK20 certified by the ak, conforming to the key interface. This allows the + // key to be verified to have been generated by the same TPM as the ak was generated with. The + // resulting key can be used for signing purposes. + return &Key{key: newWindowsKey20(hnd, name, pub, cp.CreateData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil } func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) { From 5bc739d55d6da7a9e37df5f5b04a9e6681f653cd Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Mon, 14 Nov 2022 14:18:11 +0100 Subject: [PATCH 06/20] Implement `blobs` for Windows keys --- attest/key_windows.go | 8 +++++--- attest/tpm_windows.go | 5 +---- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/attest/key_windows.go b/attest/key_windows.go index d431ad17..86f3d870 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -125,6 +125,7 @@ type windowsAK20 struct { hnd uintptr pcpKeyName string + blob []byte public []byte createData []byte createAttestation []byte @@ -224,11 +225,12 @@ func (k *windowsAK20) certify(tb tpmBase, handle interface{}, qualifyingData []b // 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, public, createData, createAttest, createSig []byte) key { +func newWindowsKey20(hnd uintptr, pcpKeyName string, blob, pub, createData, createAttest, createSig []byte) key { return &windowsAK20{ hnd: hnd, pcpKeyName: pcpKeyName, - public: public, + blob: blob, + public: pub, createData: createData, createAttestation: createAttest, createSignature: createSig, @@ -236,7 +238,7 @@ func newWindowsKey20(hnd uintptr, pcpKeyName string, public, createData, createA } func (k *windowsAK20) blobs() ([]byte, []byte, error) { - return nil, nil, errors.New("not implemented") + return k.public, k.blob, nil } func (k *windowsAK20) certificationParameters() CertificationParameters { diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 840aa9bd..6d6c851f 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -365,13 +365,10 @@ func (t *windowsTPM) newKey(ak *AK, config *KeyConfig) (*Key, error) { return nil, fmt.Errorf("access public key: %v", err) } - // TODO(hslatman): do we need the blob? - _ = blob - // Return a new windowsAK20 certified by the ak, conforming to the key interface. This allows the // key to be verified to have been generated by the same TPM as the ak was generated with. The // resulting key can be used for signing purposes. - return &Key{key: newWindowsKey20(hnd, name, pub, cp.CreateData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil + return &Key{key: newWindowsKey20(hnd, name, blob, pub, cp.CreateData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil } func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) { From eb68d9732fb5c4028a0b2d776bdc8ddac1e3656f Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Mon, 14 Nov 2022 16:15:50 +0100 Subject: [PATCH 07/20] Add `loadKey` on Windows --- attest/key_windows.go | 8 ++++---- attest/tpm_windows.go | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 36 insertions(+), 5 deletions(-) diff --git a/attest/key_windows.go b/attest/key_windows.go index 86f3d870..15a00c5b 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -125,7 +125,6 @@ type windowsAK20 struct { hnd uintptr pcpKeyName string - blob []byte public []byte createData []byte createAttestation []byte @@ -225,11 +224,10 @@ func (k *windowsAK20) certify(tb tpmBase, handle interface{}, qualifyingData []b // 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, blob, pub, createData, createAttest, createSig []byte) key { +func newWindowsKey20(hnd uintptr, pcpKeyName string, pub, createData, createAttest, createSig []byte) key { return &windowsAK20{ hnd: hnd, pcpKeyName: pcpKeyName, - blob: blob, public: pub, createData: createData, createAttestation: createAttest, @@ -238,7 +236,9 @@ func newWindowsKey20(hnd uintptr, pcpKeyName string, blob, pub, createData, crea } func (k *windowsAK20) blobs() ([]byte, []byte, error) { - return k.public, k.blob, nil + // 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 { diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 6d6c851f..1ac9c82a 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -365,13 +365,44 @@ func (t *windowsTPM) newKey(ak *AK, config *KeyConfig) (*Key, error) { return nil, fmt.Errorf("access public key: %v", err) } + // TODO(hslatman): blob not used on Windows? + _ = blob + // Return a new windowsAK20 certified by the ak, conforming to the key interface. This allows the // key to be verified to have been generated by the same TPM as the ak was generated with. The // resulting key can be used for signing purposes. - return &Key{key: newWindowsKey20(hnd, name, blob, pub, cp.CreateData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil + return &Key{key: newWindowsKey20(hnd, name, pub, cp.CreateData, cp.CreateAttestation, cp.CreateSignature), pub: pubKey, tpm: t}, nil } func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) { + if t.version != TPMVersion20 { + return nil, fmt.Errorf("loading keys on TPM version %v is unsupported", t.version) + } + sKey, err := deserializeKey(opaqueBlob, t.version) + if err != nil { + return nil, fmt.Errorf("deserializeKey() failed: %v", err) + } + if sKey.Encoding != keyEncodingOSManaged { + return nil, fmt.Errorf("unsupported key encoding: %x", sKey.Encoding) + } + + hnd, err := t.pcp.LoadKeyByName(sKey.Name) + if err != nil { + return nil, fmt.Errorf("pcp failed to load key: %v", err) + } + + tpmPub, err := tpm2.DecodePublic(sKey.Public) + if err != nil { + return nil, fmt.Errorf("decode public key: %v", err) + } + + pubKey, err := tpmPub.Key() + if err != nil { + return nil, fmt.Errorf("access public key: %v", err) + } + + return &Key{key: newWindowsKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature), pub: pubKey, tpm: t}, nil + return nil, fmt.Errorf("not implemented") } From 3543ffd4dca6897db7c1d49664b3b8df3c1b3416 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Mon, 14 Nov 2022 22:17:09 +0100 Subject: [PATCH 08/20] Remove superfluous return --- attest/tpm_windows.go | 2 -- 1 file changed, 2 deletions(-) diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 1ac9c82a..8aa5c996 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -402,8 +402,6 @@ func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) { } return &Key{key: newWindowsKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature), pub: pubKey, tpm: t}, nil - - return nil, fmt.Errorf("not implemented") } func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) { From 3737d78a69549d9fbbd15449fadea87afb417006 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Tue, 15 Nov 2022 22:58:14 +0100 Subject: [PATCH 09/20] Add `Name` and `Prefix` options to `KeyConfig` --- attest/application_key.go | 9 +++++++++ attest/tpm.go | 10 ++++++++++ attest/tpm_windows.go | 25 +++++++++++++++++++++---- 3 files changed, 40 insertions(+), 4 deletions(-) diff --git a/attest/application_key.go b/attest/application_key.go index c22192be..3f07c9d2 100644 --- a/attest/application_key.go +++ b/attest/application_key.go @@ -74,6 +74,15 @@ type KeyConfig struct { Size int QualifyingData []byte + + // Name is used to specify a name for the key, instead of generating + // a random one. If provided, the Prefix will be ignored. This property + // is only used on Windows. + Name string + + // Prefix is used to specify a custom prefix for the key, instead of + // using the default `app`. This property is only used on Windows. + Prefix string } // defaultConfig is used when no other configuration is specified. diff --git a/attest/tpm.go b/attest/tpm.go index 9ad82563..d077bb07 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -22,6 +22,7 @@ import ( "encoding/asn1" "encoding/base64" "encoding/binary" + "errors" "fmt" "io" "strings" @@ -365,6 +366,15 @@ func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) { if opts.Algorithm == "" && opts.Size == 0 { opts = defaultConfig } + if _, ok := t.tpm.(*windowsTPM); !ok && opts.Name != "" { + return nil, errors.New("providing a key name is only supported with Windows TPMs") + } + if _, ok := t.tpm.(*windowsTPM); !ok && opts.Prefix != "" { + return nil, errors.New("providing a key prefix is only supported with Windows TPMs") + } + if opts.Name != "" && opts.Prefix != "" { + return nil, errors.New("key prefix and name are incompatible") + } return t.tpm.newKey(ak, opts) } diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 8aa5c996..e356c0d7 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -335,11 +335,10 @@ func (t *windowsTPM) newKey(ak *AK, config *KeyConfig) (*Key, error) { return nil, fmt.Errorf("expected *windowsAK20, got: %T", k) } - nameHex := make([]byte, 5) - if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) { - return nil, fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err) + name, err := getKeyName(config) + if err != nil { + return nil, fmt.Errorf("failed to create name for key: %w", err) } - name := fmt.Sprintf("app-%x", nameHex) hnd, pub, blob, err := t.pcp.NewKey(name, config) if err != nil { @@ -484,3 +483,21 @@ func (t *windowsTPM) measurementLog() ([]byte, error) { } return logBuffer, nil } + +func getKeyName(config *KeyConfig) (string, error) { + if config.Name != "" { + return config.Name, nil + } + + prefix := "app" + if config.Prefix != "" { + prefix = config.Prefix + } + + nameHex := make([]byte, 5) + if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) { + return "", fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err) + } + + return fmt.Sprintf("%s-%x", prefix, nameHex), nil +} From 4dd9dc676da3146018e1bc1c9ea2ea7386465438 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Wed, 16 Nov 2022 00:54:00 +0100 Subject: [PATCH 10/20] Disable Windows TPM check for `KeyConfig` properties --- attest/tpm.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/attest/tpm.go b/attest/tpm.go index d077bb07..0474720b 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -366,12 +366,12 @@ func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) { if opts.Algorithm == "" && opts.Size == 0 { opts = defaultConfig } - if _, ok := t.tpm.(*windowsTPM); !ok && opts.Name != "" { - return nil, errors.New("providing a key name is only supported with Windows TPMs") - } - if _, ok := t.tpm.(*windowsTPM); !ok && opts.Prefix != "" { - return nil, errors.New("providing a key prefix is only supported with Windows TPMs") - } + // if _, ok := t.tpm.(*windowsTPM); !ok && opts.Name != "" { + // return nil, errors.New("providing a key name is only supported with Windows TPMs") + // } + // if _, ok := t.tpm.(*windowsTPM); !ok && opts.Prefix != "" { + // return nil, errors.New("providing a key prefix is only supported with Windows TPMs") + // } if opts.Name != "" && opts.Prefix != "" { return nil, errors.New("key prefix and name are incompatible") } From b832351a5d19d193634afc72cdf5db518e5a7c38 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Wed, 4 Jan 2023 16:40:08 +0100 Subject: [PATCH 11/20] Add `Name` and `Prefix` to AK creation --- attest/attest.go | 16 ++++++++++++---- attest/tpm_windows.go | 26 ++++++++++++++++++++++---- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/attest/attest.go b/attest/attest.go index 921fb117..bbe1a940 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -158,6 +158,14 @@ func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, er // AKConfig encapsulates parameters for minting keys. This type is defined // now (despite being empty) for future interface compatibility. type AKConfig struct { + // Name is used to specify a name for the key, instead of generating + // a random one. If provided, the Prefix will be ignored. This property + // is only used on Windows. + Name string + + // Prefix is used to specify a custom prefix for the key, instead of + // using the default `app`. This property is only used on Windows. + Prefix string } // EncryptedCredential represents encrypted parameters which must be activated @@ -398,10 +406,10 @@ func (a HashAlg) String() string { // the booted state of the machine the TPM is attached to. // // The digests contained in the event log can be considered authentic if: -// - The AK public corresponds to the known AK for that platform. -// - All quotes are verified with AKPublic.Verify(), and return no errors. -// - The event log parsed successfully using ParseEventLog(), and a call -// to EventLog.Verify() with the full set of PCRs returned no error. +// - The AK public corresponds to the known AK for that platform. +// - All quotes are verified with AKPublic.Verify(), and return no errors. +// - The event log parsed successfully using ParseEventLog(), and a call +// to EventLog.Verify() with the full set of PCRs returned no error. type PlatformParameters struct { // The version of the TPM which generated this attestation. TPMVersion TPMVersion diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index e356c0d7..023f4c65 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -276,11 +276,11 @@ func decryptCredential(secretKey, blob []byte) ([]byte, error) { } func (t *windowsTPM) newAK(opts *AKConfig) (*AK, error) { - nameHex := make([]byte, 5) - if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) { - return nil, fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err) + + name, err := getAKName(opts) + if err != nil { + return nil, fmt.Errorf("failed to create name for key: %w", err) } - name := fmt.Sprintf("ak-%x", nameHex) kh, err := t.pcp.NewAK(name) if err != nil { @@ -501,3 +501,21 @@ func getKeyName(config *KeyConfig) (string, error) { return fmt.Sprintf("%s-%x", prefix, nameHex), nil } + +func getAKName(config *AKConfig) (string, error) { + if config.Name != "" { + return config.Name, nil + } + + prefix := "ak" + if config.Prefix != "" { + prefix = config.Prefix + } + + nameHex := make([]byte, 5) + if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) { + return "", fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err) + } + + return fmt.Sprintf("%s-%x", prefix, nameHex), nil +} From 7d9b67d7351a1966e58c317054e46a2e9d9d3a02 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Wed, 4 Jan 2023 17:00:23 +0100 Subject: [PATCH 12/20] Fix missing `ECDSA` curve when signing on Windows --- attest/key_windows.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/attest/key_windows.go b/attest/key_windows.go index e9a1e6a5..c5bdff5e 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -270,9 +270,9 @@ func (k *windowsAK20) sign(tb tpmBase, digest []byte, pub crypto.PublicKey, opts return nil, fmt.Errorf("TPMKeyHandle() failed: %v", err) } - switch pub.(type) { + switch p := pub.(type) { case *ecdsa.PublicKey: - return signECDSA(rw, hnd, digest) + return signECDSA(rw, hnd, digest, p.Curve) case *rsa.PublicKey: return signRSA(rw, hnd, digest, opts) } From d197d7919ecdfb27a8624ae160068d9947c657fd Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Thu, 5 Jan 2023 12:23:00 +0100 Subject: [PATCH 13/20] Add support for deleting keys On Windows, when the key is managed by the OS, keys are stored on the filesystem. When trying to create a key with the same name, this will fail with the following error: `NCryptCreatePersistedKey returned 8009000F: The operation completed successfully.` This commit adds support for deleting these keys, so that a new key can be created with the same name. Have only tested this on Windows so far. My assumption is that for keys created with `go-attestation` on Linux, the keys aren't persisted "inside the TPM", so there's nothing to do when deleting them, except for any keys managed externally. --- attest/tpm.go | 5 +++++ attest/tpm_windows.go | 21 +++++++++++++++++++++ attest/wrapped_tpm20.go | 5 +++++ 3 files changed, 31 insertions(+) diff --git a/attest/tpm.go b/attest/tpm.go index 949f5ddc..3db7957b 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -300,6 +300,7 @@ type tpmBase interface { newAK(opts *AKConfig) (*AK, error) loadKey(opaqueBlob []byte) (*Key, error) newKey(ak *AK, opts *KeyConfig) (*Key, error) + deleteKey(opaqueBlob []byte) error pcrs(alg HashAlg) ([]PCR, error) measurementLog() ([]byte, error) } @@ -386,6 +387,10 @@ func (t *TPM) LoadKey(opaqueBlob []byte) (*Key, error) { return t.tpm.loadKey(opaqueBlob) } +func (t *TPM) DeleteKey(opaqueBlob []byte) error { + return t.tpm.deleteKey(opaqueBlob) +} + // PCRs returns the present value of Platform Configuration Registers with // the given digest algorithm. // diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 023f4c65..aa5cfb6f 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -403,6 +403,27 @@ func (t *windowsTPM) loadKey(opaqueBlob []byte) (*Key, error) { return &Key{key: newWindowsKey20(hnd, sKey.Name, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature), pub: pubKey, tpm: t}, nil } +func (t *windowsTPM) deleteKey(opaqueBlob []byte) error { + sKey, err := deserializeKey(opaqueBlob, TPMVersion20) + if err != nil { + return fmt.Errorf("deserializeKey() failed: %w", err) + } + if sKey.Encoding != keyEncodingOSManaged { + return fmt.Errorf("unsupported key encoding: %x", sKey.Encoding) + } + + hnd, err := t.pcp.LoadKeyByName(sKey.Name) + if err != nil { + return fmt.Errorf("pcp failed to load key: %w", err) + } + + if err := t.pcp.DeleteKey(hnd); err != nil { + return fmt.Errorf("delete public key: %w", err) + } + + return nil +} + func allPCRs12(tpm io.ReadWriter) (map[uint32][]byte, error) { numPCRs := 24 out := map[uint32][]byte{} diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index 6e94ea3f..dbd9eba5 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -335,6 +335,11 @@ func (t *wrappedTPM20) loadKey(opaqueBlob []byte) (*Key, error) { return &Key{key: newWrappedKey20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature), pub: pub, tpm: t}, nil } +func (t *wrappedTPM20) deleteKey(opaqueBlob []byte) error { + // assuming the *wrappedTPM doesn't store the key at all, there's nothing to do // TODO: verify if this is correct + return nil +} + func (t *wrappedTPM20) pcrs(alg HashAlg) ([]PCR, error) { PCRs, err := readAllPCRs20(t.rwc, alg.goTPMAlg()) if err != nil { From 37fd3faba57c542350f6a6877cb6c8ee17cbbc61 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Fri, 13 Jan 2023 13:40:12 +0100 Subject: [PATCH 14/20] Fix tests for TPM 1.2 --- attest/key_linux.go | 2 +- attest/tpm12_linux.go | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/attest/key_linux.go b/attest/key_linux.go index ac09e491..8669b169 100644 --- a/attest/key_linux.go +++ b/attest/key_linux.go @@ -93,6 +93,6 @@ 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") } diff --git a/attest/tpm12_linux.go b/attest/tpm12_linux.go index a2eba7d2..73561e37 100644 --- a/attest/tpm12_linux.go +++ b/attest/tpm12_linux.go @@ -112,6 +112,10 @@ func (t *trousersTPM) loadKey(opaqueBlob []byte) (*Key, error) { return nil, fmt.Errorf("not implemented") } +func (t *trousersTPM) deleteKey(opaqueBlob []byte) (*Key, error) { + return nil, fmt.Errorf("not implemented") +} + func (t *trousersTPM) newAK(opts *AKConfig) (*AK, error) { pub, blob, err := attestation.CreateAIK(t.ctx) if err != nil { From 0ad94dd6a52ee9f3275e8d0049ffbcd75ec212c8 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Fri, 13 Jan 2023 14:00:42 +0100 Subject: [PATCH 15/20] Fix tests for TPM 1.2 --- attest/tpm12_linux.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/attest/tpm12_linux.go b/attest/tpm12_linux.go index 73561e37..91e0c2f3 100644 --- a/attest/tpm12_linux.go +++ b/attest/tpm12_linux.go @@ -112,8 +112,8 @@ func (t *trousersTPM) loadKey(opaqueBlob []byte) (*Key, error) { return nil, fmt.Errorf("not implemented") } -func (t *trousersTPM) deleteKey(opaqueBlob []byte) (*Key, error) { - return nil, fmt.Errorf("not implemented") +func (t *trousersTPM) deleteKey(opaqueBlob []byte) error { + return fmt.Errorf("not implemented") } func (t *trousersTPM) newAK(opts *AKConfig) (*AK, error) { From 0ea71a159078f1c85e31a87ec0621c3c373f6448 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Fri, 24 Feb 2023 11:52:14 +0100 Subject: [PATCH 16/20] Add `DeleteAK` method --- attest/tpm.go | 5 +++++ attest/tpm_windows.go | 21 +++++++++++++++++++++ attest/wrapped_tpm20.go | 5 +++++ 3 files changed, 31 insertions(+) diff --git a/attest/tpm.go b/attest/tpm.go index 8da235f1..24bb5fc5 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -301,6 +301,7 @@ type tpmBase interface { loadAK(opaqueBlob []byte) (*AK, error) newAK(opts *AKConfig) (*AK, error) + deleteAK(opaqueBlob []byte) error loadKey(opaqueBlob []byte) (*Key, error) newKey(ak *AK, opts *KeyConfig) (*Key, error) deleteKey(opaqueBlob []byte) error @@ -361,6 +362,10 @@ func (t *TPM) NewAK(opts *AKConfig) (*AK, error) { return t.tpm.newAK(opts) } +func (t *TPM) DeleteAK(opaqueBlob []byte) error { + return t.tpm.deleteAK(opaqueBlob) +} + // NewKey creates an application key certified by the attestation key. If opts is nil // then DefaultConfig is used. func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) { diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index aa5cfb6f..84454b2a 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -326,6 +326,27 @@ func (t *windowsTPM) loadAK(opaqueBlob []byte) (*AK, error) { } } +func (t *windowsTPM) deleteAK(opaqueBlob []byte) error { + sKey, err := deserializeKey(opaqueBlob, TPMVersion20) + if err != nil { + return fmt.Errorf("deserializeKey() failed: %w", err) + } + if sKey.Encoding != keyEncodingOSManaged { + return fmt.Errorf("unsupported key encoding: %x", sKey.Encoding) + } + + hnd, err := t.pcp.LoadKeyByName(sKey.Name) + if err != nil { + return fmt.Errorf("pcp failed to load key: %w", err) + } + + if err := t.pcp.DeleteKey(hnd); err != nil { + return fmt.Errorf("delete public key: %w", err) + } + + return nil +} + func (t *windowsTPM) newKey(ak *AK, config *KeyConfig) (*Key, error) { if t.version != TPMVersion20 { return nil, fmt.Errorf("key generation on TPM version %v is unsupported", t.version) diff --git a/attest/wrapped_tpm20.go b/attest/wrapped_tpm20.go index dbd9eba5..8d1e1f0e 100644 --- a/attest/wrapped_tpm20.go +++ b/attest/wrapped_tpm20.go @@ -319,6 +319,11 @@ func (t *wrappedTPM20) loadAK(opaqueBlob []byte) (*AK, error) { return &AK{ak: newWrappedAK20(hnd, sKey.Blob, sKey.Public, sKey.CreateData, sKey.CreateAttestation, sKey.CreateSignature)}, nil } +func (t *wrappedTPM20) deleteAK(opaqueBlob []byte) error { + // assuming the *wrappedTPM doesn't store the key at all, there's nothing to do // TODO: verify if this is correct + return nil +} + func (t *wrappedTPM20) loadKey(opaqueBlob []byte) (*Key, error) { hnd, sKey, err := t.deserializeAndLoad(opaqueBlob) if err != nil { From eb81e6e998b76d18328eb381f753e653a3e5baef Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Fri, 24 Feb 2023 12:59:51 +0100 Subject: [PATCH 17/20] Add `Blobs` method for `AK` --- attest/attest.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/attest/attest.go b/attest/attest.go index bbe1a940..a7d6d8d9 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -105,6 +105,7 @@ type ak interface { quote(t tpmBase, nonce []byte, alg HashAlg) (*Quote, error) attestationParameters() AttestationParameters certify(tb tpmBase, handle interface{}, qualifyingData []byte) (*CertificationParameters, error) + blobs() ([]byte, []byte, error) } // AK represents a key which can be used for attestation. @@ -155,6 +156,11 @@ func (k *AK) Certify(tpm *TPM, handle interface{}) (*CertificationParameters, er 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. This type is defined // now (despite being empty) for future interface compatibility. type AKConfig struct { From 1bcb20a75adda68db0dae97ecc48b02241758284 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Fri, 24 Feb 2023 13:10:42 +0100 Subject: [PATCH 18/20] Add missing methods for TPM 1.2 --- attest/key_linux.go | 5 +++++ attest/key_windows.go | 4 ++++ attest/tpm12_linux.go | 4 ++++ 3 files changed, 13 insertions(+) diff --git a/attest/key_linux.go b/attest/key_linux.go index 8669b169..1a520f82 100644 --- a/attest/key_linux.go +++ b/attest/key_linux.go @@ -18,6 +18,7 @@ package attest import ( + "errors" "fmt" "github.com/google/go-tspi/attestation" @@ -96,3 +97,7 @@ func (k *trousersKey12) attestationParameters() AttestationParameters { 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") +} diff --git a/attest/key_windows.go b/attest/key_windows.go index c5bdff5e..af02c2cf 100644 --- a/attest/key_windows.go +++ b/attest/key_windows.go @@ -120,6 +120,10 @@ func (k *windowsAK12) certify(tb tpmBase, handle interface{}, qualifyingData []b return nil, fmt.Errorf("not implemented") } +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 diff --git a/attest/tpm12_linux.go b/attest/tpm12_linux.go index 91e0c2f3..bed9cbd8 100644 --- a/attest/tpm12_linux.go +++ b/attest/tpm12_linux.go @@ -136,6 +136,10 @@ func (t *trousersTPM) loadAK(opaqueBlob []byte) (*AK, error) { return &AK{ak: newTrousersKey12(sKey.Blob, sKey.Public)}, nil } +func (t *trousersTPM) deleteAK(opaqueBlob []byte) error { + return fmt.Errorf("not implemented") +} + // allPCRs12 returns a map of all the PCR values on the TPM func allPCRs12(ctx *tspi.Context) (map[uint32][]byte, error) { tpm := ctx.GetTPM() From ef181aa8d8afc1e8c04fbfae04c1da771ea4900e Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Wed, 10 May 2023 01:14:50 +0200 Subject: [PATCH 19/20] Remove key prefix and some cleanup --- attest/application_key.go | 12 ++++-------- attest/attest.go | 10 ++-------- attest/tpm.go | 13 ++----------- attest/tpm_windows.go | 14 ++------------ 4 files changed, 10 insertions(+), 39 deletions(-) diff --git a/attest/application_key.go b/attest/application_key.go index 3f07c9d2..daf7ec84 100644 --- a/attest/application_key.go +++ b/attest/application_key.go @@ -72,17 +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. If provided, the Prefix will be ignored. This property - // is only used on Windows. + // a random one. This property is only used on Windows. Name string - - // Prefix is used to specify a custom prefix for the key, instead of - // using the default `app`. This property is only used on Windows. - Prefix string } // defaultConfig is used when no other configuration is specified. diff --git a/attest/attest.go b/attest/attest.go index a7d6d8d9..f4728949 100644 --- a/attest/attest.go +++ b/attest/attest.go @@ -161,17 +161,11 @@ func (k *AK) Blobs() (pub, priv []byte, err error) { return k.ak.blobs() } -// AKConfig encapsulates parameters for minting keys. This type is defined -// now (despite being empty) for future interface compatibility. +// 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. If provided, the Prefix will be ignored. This property - // is only used on Windows. + // a random one. This property is only used on Windows. Name string - - // Prefix is used to specify a custom prefix for the key, instead of - // using the default `app`. This property is only used on Windows. - Prefix string } // EncryptedCredential represents encrypted parameters which must be activated diff --git a/attest/tpm.go b/attest/tpm.go index 24bb5fc5..53594c9e 100644 --- a/attest/tpm.go +++ b/attest/tpm.go @@ -22,7 +22,6 @@ import ( "encoding/asn1" "encoding/base64" "encoding/binary" - "errors" "fmt" "io" "strings" @@ -373,16 +372,8 @@ func (t *TPM) NewKey(ak *AK, opts *KeyConfig) (*Key, error) { opts = defaultConfig } if opts.Algorithm == "" && opts.Size == 0 { - opts = defaultConfig - } - // if _, ok := t.tpm.(*windowsTPM); !ok && opts.Name != "" { - // return nil, errors.New("providing a key name is only supported with Windows TPMs") - // } - // if _, ok := t.tpm.(*windowsTPM); !ok && opts.Prefix != "" { - // return nil, errors.New("providing a key prefix is only supported with Windows TPMs") - // } - if opts.Name != "" && opts.Prefix != "" { - return nil, errors.New("key prefix and name are incompatible") + opts.Algorithm = defaultConfig.Algorithm + opts.Size = defaultConfig.Size } return t.tpm.newKey(ak, opts) } diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index 84454b2a..e3ab2ea1 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -531,17 +531,12 @@ func getKeyName(config *KeyConfig) (string, error) { return config.Name, nil } - prefix := "app" - if config.Prefix != "" { - prefix = config.Prefix - } - nameHex := make([]byte, 5) if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) { return "", fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err) } - return fmt.Sprintf("%s-%x", prefix, nameHex), nil + return fmt.Sprintf("app-%x", nameHex), nil } func getAKName(config *AKConfig) (string, error) { @@ -549,15 +544,10 @@ func getAKName(config *AKConfig) (string, error) { return config.Name, nil } - prefix := "ak" - if config.Prefix != "" { - prefix = config.Prefix - } - nameHex := make([]byte, 5) if n, err := rand.Read(nameHex); err != nil || n != len(nameHex) { return "", fmt.Errorf("rand.Read() failed with %d/%d bytes read and error: %v", n, len(nameHex), err) } - return fmt.Sprintf("%s-%x", prefix, nameHex), nil + return fmt.Sprintf("ak-%x", nameHex), nil } From ce1f4b5d06fa7762c30c5cb65df55aae3bfef176 Mon Sep 17 00:00:00 2001 From: Herman Slatman <hermanslatman@hotmail.com> Date: Tue, 27 Jun 2023 11:54:42 +0200 Subject: [PATCH 20/20] Fix legacy TPM2 path for Windows key creation and loading --- attest/tpm_windows.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/attest/tpm_windows.go b/attest/tpm_windows.go index faa31667..bc65ba72 100644 --- a/attest/tpm_windows.go +++ b/attest/tpm_windows.go @@ -29,8 +29,8 @@ import ( "io" "math/big" + "github.com/google/go-tpm/legacy/tpm2" tpm1 "github.com/google/go-tpm/tpm" - "github.com/google/go-tpm/tpm2" tpmtbs "github.com/google/go-tpm/tpmutil/tbs" "golang.org/x/sys/windows" )