diff --git a/tpm2/encoding_test.go b/tpm2/encoding_test.go index 4462e4e8..1f79fb16 100644 --- a/tpm2/encoding_test.go +++ b/tpm2/encoding_test.go @@ -54,7 +54,7 @@ func TestEncodeDecodeCreationData(t *testing.T) { OutsideInfo: []byte{7, 8, 9}, } - encoded, err := cd.encode() + encoded, err := cd.EncodeCreationData() if err != nil { t.Fatalf("error encoding CreationData: %v", err) } @@ -169,7 +169,7 @@ func TestDecodeLoad(t *testing.T) { } func TestEncodeCreate(t *testing.T) { - testCmdBytes, err := hex.DecodeString("80020000004d00000131400000010000000940000009000001000000090004010203040001FF001a0001000400030072000000060080004300100400000100010000000000000001000403800000") + testCmdBytes, err := hex.DecodeString("80020000004d00000131400000010000000940000009000001000000090004010203040001ff001a000100040003007200000006008000430010040000010001000000045445535400000001000403800000") if err != nil { t.Fatal(err) } @@ -188,7 +188,7 @@ func TestEncodeCreate(t *testing.T) { }, } auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession} - cmdBytes, err := encodeCreate(HandleOwner, pcrSelection7, auth, defaultPassword, []byte{255} /*sensitiveData*/, params) + cmdBytes, err := encodeCreate(HandleOwner, pcrSelection7, auth, defaultPassword, []byte{255} /*sensitiveData*/, params, []byte("TEST") /*OutsideInfo*/) if err != nil { t.Fatal(err) } diff --git a/tpm2/structures.go b/tpm2/structures.go index 4040a23a..3c863e83 100644 --- a/tpm2/structures.go +++ b/tpm2/structures.go @@ -674,7 +674,7 @@ func DecodeAttestationData(in []byte) (*AttestationData, error) { return nil, fmt.Errorf("decoding AttestedQuoteInfo: %v", err) } default: - return nil, fmt.Errorf("only Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) + return nil, fmt.Errorf("only Quote, Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) } return &ad, nil @@ -705,8 +705,12 @@ func (ad AttestationData) Encode() ([]byte, error) { if info, err = ad.AttestedCreationInfo.encode(); err != nil { return nil, fmt.Errorf("encoding AttestedCreationInfo: %v", err) } + case TagAttestQuote: + if info, err = ad.AttestedQuoteInfo.encode(); err != nil { + return nil, fmt.Errorf("encoding AttestedQuoteInfo: %v", err) + } default: - return nil, fmt.Errorf("only Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) + return nil, fmt.Errorf("only Quote, Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) } return concat(head, signer, tail, info) @@ -807,6 +811,20 @@ func decodeQuoteInfo(in *bytes.Buffer) (*QuoteInfo, error) { return &out, nil } +func (qi QuoteInfo) encode() ([]byte, error) { + sel, err := encodeTPMLPCRSelection(qi.PCRSelection) + if err != nil { + return nil, fmt.Errorf("encoding PCRSelection: %v", err) + } + + digest, err := tpmutil.Pack(qi.PCRDigest) + if err != nil { + return nil, fmt.Errorf("encoding PCRDigest: %v", err) + } + + return concat(sel, digest) +} + // IDObject represents an encrypted credential bound to a TPM object. type IDObject struct { IntegrityHMAC tpmutil.U16Bytes @@ -827,7 +845,8 @@ type CreationData struct { OutsideInfo tpmutil.U16Bytes } -func (cd *CreationData) encode() ([]byte, error) { +// EncodeCreationData encodes byte array to TPMS_CREATION_DATA message. +func (cd *CreationData) EncodeCreationData() ([]byte, error) { sel, err := encodeTPMLPCRSelection(cd.PCRSelection) if err != nil { return nil, fmt.Errorf("encoding PCRSelection: %v", err) diff --git a/tpm2/test/tpm2_test.go b/tpm2/test/tpm2_test.go index 208e6847..14ab0d6a 100644 --- a/tpm2/test/tpm2_test.go +++ b/tpm2/test/tpm2_test.go @@ -27,7 +27,6 @@ import ( "fmt" "hash" "io" - "math/big" "reflect" "strings" "testing" @@ -610,14 +609,19 @@ func TestCertify(t *testing.T) { } defer FlushContext(rw, subjectHandle) - attest, sig, err := Certify(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil) + attest, sigRaw, err := Certify(rw, defaultPassword, defaultPassword, subjectHandle, signerHandle, nil) if err != nil { t.Errorf("Certify failed: %s", err) return } + sig, err := DecodeSignature(bytes.NewBuffer(sigRaw)) + if err != nil { + t.Errorf("DecodeSignature failed: %s", err) + return + } attestHash := sha256.Sum256(attest) - if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig); err != nil { + if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig.RSA.Signature); err != nil { t.Errorf("Signature verification failed: %v", err) } @@ -736,14 +740,19 @@ func TestCertifyExternalKey(t *testing.T) { } defer FlushContext(rw, subjectHandle) - attest, sig, err := Certify(rw, emptyPassword, defaultPassword, subjectHandle, signerHandle, nil) + attest, sigRaw, err := Certify(rw, emptyPassword, defaultPassword, subjectHandle, signerHandle, nil) if err != nil { t.Errorf("Certify failed: %s", err) return } + sig, err := DecodeSignature(bytes.NewBuffer(sigRaw)) + if err != nil { + t.Errorf("DecodeSignature failed: %s", err) + return + } attestHash := sha256.Sum256(attest) - if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig); err != nil { + if err := rsa.VerifyPKCS1v15(signerPub.(*rsa.PublicKey), crypto.SHA256, attestHash[:], sig.RSA.Signature); err != nil { t.Errorf("Signature verification failed: %v", err) } } @@ -1227,7 +1236,7 @@ func TestCreateAndCertifyCreation(t *testing.T) { defer FlushContext(rw, keyHandle) scheme := SigScheme{Alg: AlgRSASSA, Hash: AlgSHA256, Count: 0} - attestation, signature, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, scheme, tix) + attestation, sigRaw, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, scheme, tix) if err != nil { t.Fatalf("CertifyCreation failed: %s", err) } @@ -1235,6 +1244,11 @@ func TestCreateAndCertifyCreation(t *testing.T) { if err != nil { t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err) } + signature, err := DecodeSignature(bytes.NewBuffer(sigRaw)) + if err != nil { + t.Errorf("DecodeSignature failed: %s", err) + return + } if att.Type != TagAttestCreation { t.Errorf("Got att.Type = %v, want TagAttestCreation", att.Type) } @@ -1254,7 +1268,7 @@ func TestCreateAndCertifyCreation(t *testing.T) { rsaPub := rsa.PublicKey{E: int(p.RSAParameters.Exponent()), N: p.RSAParameters.Modulus()} hsh := crypto.SHA256.New() hsh.Write(attestation) - if err := rsa.VerifyPKCS1v15(&rsaPub, crypto.SHA256, hsh.Sum(nil), signature); err != nil { + if err := rsa.VerifyPKCS1v15(&rsaPub, crypto.SHA256, hsh.Sum(nil), signature.RSA.Signature); err != nil { t.Errorf("VerifyPKCS1v15 failed: %v", err) } } @@ -1281,7 +1295,7 @@ func TestCreateAndCertifyCreationECC(t *testing.T) { defer FlushContext(rw, keyHandle) scheme := SigScheme{Alg: AlgECDSA, Hash: AlgSHA256, Count: 0} - attestation, signature, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, scheme, tix) + attestation, sigRaw, err := CertifyCreation(rw, emptyPassword, keyHandle, keyHandle, nil, creationHash, scheme, tix) if err != nil { t.Fatalf("CertifyCreation failed: %s", err) } @@ -1290,6 +1304,11 @@ func TestCreateAndCertifyCreationECC(t *testing.T) { if err != nil { t.Fatalf("DecodeAttestationData(%v) failed: %v", attestation, err) } + signature, err := DecodeSignature(bytes.NewBuffer(sigRaw)) + if err != nil { + t.Errorf("DecodeSignature failed: %s", err) + return + } if att.Type != TagAttestCreation { t.Errorf("Got att.Type = %v, want TagAttestCreation", att.Type) } @@ -1317,12 +1336,7 @@ func TestCreateAndCertifyCreationECC(t *testing.T) { hsh = signHash.New() hsh.Write(attestation) - r := new(big.Int) - s := new(big.Int) - r.SetBytes(signature[:32]) - s.SetBytes(signature[32:]) - - if !ecdsa.Verify(&pkEcdsa, hsh.Sum(nil), r, s) { + if !ecdsa.Verify(&pkEcdsa, hsh.Sum(nil), signature.ECC.R, signature.ECC.S) { t.Fatalf("Verify failed") } } diff --git a/tpm2/tpm2.go b/tpm2/tpm2.go index 08b8a55e..7a1c0680 100644 --- a/tpm2/tpm2.go +++ b/tpm2/tpm2.go @@ -375,7 +375,7 @@ func encodeSensitiveArea(s tpmsSensitiveCreate) ([]byte, error) { } // encodeCreate works for both TPM2_Create and TPM2_CreatePrimary. -func encodeCreate(owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, sensitiveData []byte, pub Public) ([]byte, error) { +func encodeCreate(owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, sensitiveData []byte, pub Public, outsideInfo []byte) ([]byte, error) { parent, err := tpmutil.Pack(owner) if err != nil { return nil, err @@ -399,7 +399,7 @@ func encodeCreate(owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, owne if err != nil { return nil, err } - outsideInfo, err := tpmutil.Pack(tpmutil.U16Bytes(nil)) + outsideInfoBlob, err := tpmutil.Pack(tpmutil.U16Bytes(outsideInfo)) if err != nil { return nil, err } @@ -412,7 +412,7 @@ func encodeCreate(owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, owne encodedAuth, inSensitive, publicBlob, - outsideInfo, + outsideInfoBlob, creationPCR, ) } @@ -462,7 +462,7 @@ func CreatePrimary(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, par // are returned, and they are returned in relatively raw form. func CreatePrimaryEx(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (keyHandle tpmutil.Handle, public, creationData, creationHash []byte, ticket Ticket, creationName []byte, err error) { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} - Cmd, err := encodeCreate(owner, sel, auth, ownerPassword, nil /*inSensitive*/, pub) + Cmd, err := encodeCreate(owner, sel, auth, ownerPassword, nil /*inSensitive*/, pub, nil /*OutsideInfo*/) if err != nil { return 0, nil, nil, nil, Ticket{}, nil, err } @@ -527,8 +527,8 @@ func decodeCreate(in []byte) (private, public, creationData, creationHash tpmuti return private, public, creationData, creationHash, creationTicket, nil } -func create(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, objectPassword string, sensitiveData []byte, pub Public, pcrSelection PCRSelection) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { - cmd, err := encodeCreate(parentHandle, pcrSelection, auth, objectPassword, sensitiveData, pub) +func create(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, objectPassword string, sensitiveData []byte, pub Public, pcrSelection PCRSelection, outsideInfo []byte) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { + cmd, err := encodeCreate(parentHandle, pcrSelection, auth, objectPassword, sensitiveData, pub, outsideInfo) if err != nil { return nil, nil, nil, nil, Ticket{}, err } @@ -544,21 +544,28 @@ func create(rw io.ReadWriter, parentHandle tpmutil.Handle, auth AuthCommand, obj // creation data, a hash of said data and the creation ticket. func CreateKey(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} - return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel) + return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel, nil /*OutsideInfo*/) } // CreateKeyUsingAuth creates a new key pair under the owner handle using the // provided AuthCommand. Returns private key and public key blobs as well as // the creation data, a hash of said data, and the creation ticket. func CreateKeyUsingAuth(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, auth AuthCommand, ownerPassword string, pub Public) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { - return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel) + return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel, nil /*OutsideInfo*/) } // CreateKeyWithSensitive is very similar to CreateKey, except // that it can take in a piece of sensitive data. func CreateKeyWithSensitive(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public, sensitive []byte) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} - return create(rw, owner, auth, ownerPassword, sensitive, pub, sel) + return create(rw, owner, auth, ownerPassword, sensitive, pub, sel, nil /*OutsideInfo*/) +} + +// CreateKeyWithOutsideInfo is very similar to CreateKey, except +// that it returns the outside information. +func CreateKeyWithOutsideInfo(rw io.ReadWriter, owner tpmutil.Handle, sel PCRSelection, parentPassword, ownerPassword string, pub Public, outsideInfo []byte) (private, public, creationData, creationHash []byte, creationTicket Ticket, err error) { + auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} + return create(rw, owner, auth, ownerPassword, nil /*inSensitive*/, pub, sel, outsideInfo) } // Seal creates a data blob object that seals the sensitive data under a parent and with a @@ -572,7 +579,7 @@ func Seal(rw io.ReadWriter, parentHandle tpmutil.Handle, parentPassword, objectP AuthPolicy: objectAuthPolicy, } auth := AuthCommand{Session: HandlePasswordSession, Attributes: AttrContinueSession, Auth: []byte(parentPassword)} - private, public, _, _, _, err := create(rw, parentHandle, auth, objectPassword, sensitiveData, inPublic, PCRSelection{}) + private, public, _, _, _, err := create(rw, parentHandle, auth, objectPassword, sensitiveData, inPublic, PCRSelection{}, nil /*OutsideInfo*/) if err != nil { return nil, nil, err } @@ -1746,35 +1753,17 @@ func encodeCertifyEx(objectAuth, signerAuth string, object, signer tpmutil.Handl func decodeCertify(resp []byte) ([]byte, []byte, error) { var paramSize uint32 - var attest, signature tpmutil.U16Bytes - var sigAlg, hashAlg Algorithm + var attest tpmutil.U16Bytes buf := bytes.NewBuffer(resp) if err := tpmutil.UnpackBuf(buf, ¶mSize); err != nil { return nil, nil, err } buf.Truncate(int(paramSize)) - if err := tpmutil.UnpackBuf(buf, &attest, &sigAlg); err != nil { + if err := tpmutil.UnpackBuf(buf, &attest); err != nil { return nil, nil, err } - // If sigAlg is AlgNull, there will be no hashAlg or signature. - // This will happen if AlgNull was passed in the Certify() as - // the signing key (no need to sign the response). - // See TPM2 spec part4 pg227 SignAttestInfo() - if sigAlg != AlgNull { - if sigAlg == AlgECDSA { - var r, s tpmutil.U16Bytes - if err := tpmutil.UnpackBuf(buf, &hashAlg, &r, &s); err != nil { - return nil, nil, err - } - signature = append(r, s...) - } else { - if err := tpmutil.UnpackBuf(buf, &hashAlg, &signature); err != nil { - return nil, nil, err - } - } - } - return attest, signature, nil + return attest, buf.Bytes(), nil } // Certify generates a signature of a loaded TPM object with a signing key diff --git a/tpmutil/run.go b/tpmutil/run.go index 21a9c665..e14270af 100644 --- a/tpmutil/run.go +++ b/tpmutil/run.go @@ -19,6 +19,7 @@ import ( "errors" "io" "os" + "time" ) // maxTPMResponse is the largest possible response from the TPM. We need to know @@ -41,32 +42,51 @@ func RunCommand(rw io.ReadWriter, tag Tag, cmd Command, in ...interface{}) ([]by return nil, 0, err } - if _, err := rw.Write(inb); err != nil { - return nil, 0, err - } + // f(t) = (2^t)ms, up to 2s + var backoffFac uint = 0 + var rh responseHeader + var outb []byte - // If the TPM is a real device, it may not be ready for reading immediately after writing - // the command. Wait until the file descriptor is ready to be read from. - if f, ok := rw.(*os.File); ok { - if err = poll(f); err != nil { + for { + if _, err := rw.Write(inb); err != nil { return nil, 0, err } - } - outb := make([]byte, maxTPMResponse) - outlen, err := rw.Read(outb) - if err != nil { - return nil, 0, err - } - // Resize the buffer to match the amount read from the TPM. - outb = outb[:outlen] + // If the TPM is a real device, it may not be ready for reading immediately after writing + // the command. Wait until the file descriptor is ready to be read from. + if f, ok := rw.(*os.File); ok { + if err = poll(f); err != nil { + return nil, 0, err + } + } - var rh responseHeader - read, err := Unpack(outb, &rh) - if err != nil { - return nil, 0, err + outb = make([]byte, maxTPMResponse) + outlen, err := rw.Read(outb) + if err != nil { + return nil, 0, err + } + // Resize the buffer to match the amount read from the TPM. + outb = outb[:outlen] + + read, err := Unpack(outb, &rh) + if err != nil { + return nil, 0, err + } + outb = outb[read:] + + // In case TPM is busy retry the command after waiting a few miliseconds + if rh.Res == RCRetry { + if backoffFac < 11 { + dur := (1 << backoffFac) * time.Millisecond + time.Sleep(dur) + backoffFac++ + } else { + return nil, 0, err + } + } else { + break + } } - outb = outb[read:] if rh.Res != RCSuccess { return nil, rh.Res, nil diff --git a/tpmutil/structures.go b/tpmutil/structures.go index d7d05fb4..c9455352 100644 --- a/tpmutil/structures.go +++ b/tpmutil/structures.go @@ -145,6 +145,9 @@ type ResponseCode uint32 // 2.0. const RCSuccess ResponseCode = 0x000 +// RCRetry is response code for TPM is busy. +const RCRetry ResponseCode = 0x922 + // A responseHeader is a header for TPM responses. type responseHeader struct { Tag Tag