diff --git a/tpm2/encoding_test.go b/tpm2/encoding_test.go index 4462e4e8..66509ab9 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) } @@ -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")) if err != nil { t.Fatal(err) } diff --git a/tpm2/structures.go b/tpm2/structures.go index 4040a23a..8adcdf4d 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 Ouote, Certify & Creation attestation structures are supported, got type 0x%x", ad.Type) } return &ad, nil @@ -705,6 +705,10 @@ 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) } @@ -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 Name: %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/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