diff options
Diffstat (limited to 'vendor/github.com/duo-labs/webauthn/protocol/googletpm/certinfo.go')
-rw-r--r-- | vendor/github.com/duo-labs/webauthn/protocol/googletpm/certinfo.go | 282 |
1 files changed, 282 insertions, 0 deletions
diff --git a/vendor/github.com/duo-labs/webauthn/protocol/googletpm/certinfo.go b/vendor/github.com/duo-labs/webauthn/protocol/googletpm/certinfo.go new file mode 100644 index 0000000000..08ae3a9933 --- /dev/null +++ b/vendor/github.com/duo-labs/webauthn/protocol/googletpm/certinfo.go @@ -0,0 +1,282 @@ +package googletpm + +import ( + "bytes" + "crypto/sha1" + "crypto/sha256" + "crypto/sha512" + "fmt" + "hash" +) + +// DecodeAttestationData decode a TPMS_ATTEST message. No error is returned if +// the input has extra trailing data. +func DecodeAttestationData(in []byte) (*AttestationData, error) { + buf := bytes.NewBuffer(in) + + var ad AttestationData + if err := UnpackBuf(buf, &ad.Magic, &ad.Type); err != nil { + return nil, fmt.Errorf("decoding Magic/Type: %v", err) + } + n, err := decodeName(buf) + if err != nil { + return nil, fmt.Errorf("decoding QualifiedSigner: %v", err) + } + ad.QualifiedSigner = *n + if err := UnpackBuf(buf, &ad.ExtraData, &ad.ClockInfo, &ad.FirmwareVersion); err != nil { + return nil, fmt.Errorf("decoding ExtraData/ClockInfo/FirmwareVersion: %v", err) + } + + // The spec specifies several other types of attestation data. We only need + // parsing of Certify & Creation attestation data for now. If you need + // support for other attestation types, add them here. + switch ad.Type { + case TagAttestCertify: + if ad.AttestedCertifyInfo, err = decodeCertifyInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedCertifyInfo: %v", err) + } + case TagAttestCreation: + if ad.AttestedCreationInfo, err = decodeCreationInfo(buf); err != nil { + return nil, fmt.Errorf("decoding AttestedCreationInfo: %v", err) + } + case TagAttestQuote: + if ad.AttestedQuoteInfo, err = decodeQuoteInfo(buf); err != nil { + 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 &ad, nil +} + +// AttestationData contains data attested by TPM commands (like Certify). +type AttestationData struct { + Magic uint32 + Type Tag + QualifiedSigner Name + ExtraData []byte + ClockInfo ClockInfo + FirmwareVersion uint64 + AttestedCertifyInfo *CertifyInfo + AttestedQuoteInfo *QuoteInfo + AttestedCreationInfo *CreationInfo +} + +// Tag is a command tag. +type Tag uint16 + +type Name struct { + Handle *Handle + Digest *HashValue +} + +// A Handle is a reference to a TPM object. +type Handle uint32 +type HashValue struct { + Alg Algorithm + Value []byte +} + +// ClockInfo contains TPM state info included in AttestationData. +type ClockInfo struct { + Clock uint64 + ResetCount uint32 + RestartCount uint32 + Safe byte +} + +// CertifyInfo contains Certify-specific data for TPMS_ATTEST. +type CertifyInfo struct { + Name Name + QualifiedName Name +} + +// QuoteInfo represents a TPMS_QUOTE_INFO structure. +type QuoteInfo struct { + PCRSelection PCRSelection + PCRDigest []byte +} + +// PCRSelection contains a slice of PCR indexes and a hash algorithm used in +// them. +type PCRSelection struct { + Hash Algorithm + PCRs []int +} + +// CreationInfo contains Creation-specific data for TPMS_ATTEST. +type CreationInfo struct { + Name Name + // Most TPM2B_Digest structures contain a TPMU_HA structure + // and get parsed to HashValue. This is never the case for the + // digest in TPMS_CREATION_INFO. + OpaqueDigest []byte +} + +func decodeName(in *bytes.Buffer) (*Name, error) { + var nameBuf []byte + if err := UnpackBuf(in, &nameBuf); err != nil { + return nil, err + } + + name := new(Name) + switch len(nameBuf) { + case 0: + // No name is present. + case 4: + name.Handle = new(Handle) + if err := UnpackBuf(bytes.NewBuffer(nameBuf), name.Handle); err != nil { + return nil, fmt.Errorf("decoding Handle: %v", err) + } + default: + var err error + name.Digest, err = decodeHashValue(bytes.NewBuffer(nameBuf)) + if err != nil { + return nil, fmt.Errorf("decoding Digest: %v", err) + } + } + return name, nil +} + +func decodeHashValue(in *bytes.Buffer) (*HashValue, error) { + var hv HashValue + if err := UnpackBuf(in, &hv.Alg); err != nil { + return nil, fmt.Errorf("decoding Alg: %v", err) + } + hfn, ok := hashConstructors[hv.Alg] + if !ok { + return nil, fmt.Errorf("unsupported hash algorithm type 0x%x", hv.Alg) + } + hv.Value = make([]byte, hfn().Size()) + if _, err := in.Read(hv.Value); err != nil { + return nil, fmt.Errorf("decoding Value: %v", err) + } + return &hv, nil +} + +// HashConstructor returns a function that can be used to make a +// hash.Hash using the specified algorithm. An error is returned +// if the algorithm is not a hash algorithm. +func (a Algorithm) HashConstructor() (func() hash.Hash, error) { + c, ok := hashConstructors[a] + if !ok { + return nil, fmt.Errorf("algorithm not supported: 0x%x", a) + } + return c, nil +} + +var hashConstructors = map[Algorithm]func() hash.Hash{ + AlgSHA1: sha1.New, + AlgSHA256: sha256.New, + AlgSHA384: sha512.New384, + AlgSHA512: sha512.New, +} + +// TPM Structure Tags. Tags are used to disambiguate structures, similar to Alg +// values: tag value defines what kind of data lives in a nested field. +const ( + TagNull Tag = 0x8000 + TagNoSessions Tag = 0x8001 + TagSessions Tag = 0x8002 + TagAttestCertify Tag = 0x8017 + TagAttestQuote Tag = 0x8018 + TagAttestCreation Tag = 0x801a + TagHashCheck Tag = 0x8024 +) + +func decodeCertifyInfo(in *bytes.Buffer) (*CertifyInfo, error) { + var ci CertifyInfo + + n, err := decodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding Name: %v", err) + } + ci.Name = *n + + n, err = decodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding QualifiedName: %v", err) + } + ci.QualifiedName = *n + + return &ci, nil +} + +func decodeCreationInfo(in *bytes.Buffer) (*CreationInfo, error) { + var ci CreationInfo + + n, err := decodeName(in) + if err != nil { + return nil, fmt.Errorf("decoding Name: %v", err) + } + ci.Name = *n + + if err := UnpackBuf(in, &ci.OpaqueDigest); err != nil { + return nil, fmt.Errorf("decoding Digest: %v", err) + } + + return &ci, nil +} + +func decodeQuoteInfo(in *bytes.Buffer) (*QuoteInfo, error) { + var out QuoteInfo + sel, err := decodeTPMLPCRSelection(in) + if err != nil { + return nil, fmt.Errorf("decoding PCRSelection: %v", err) + } + out.PCRSelection = sel + if err := UnpackBuf(in, &out.PCRDigest); err != nil { + return nil, fmt.Errorf("decoding PCRDigest: %v", err) + } + return &out, nil +} + +func decodeTPMLPCRSelection(buf *bytes.Buffer) (PCRSelection, error) { + var count uint32 + var sel PCRSelection + if err := UnpackBuf(buf, &count); err != nil { + return sel, err + } + switch count { + case 0: + sel.Hash = AlgUnknown + return sel, nil + case 1: // We only support decoding of a single PCRSelection. + default: + return sel, fmt.Errorf("decoding TPML_PCR_SELECTION list longer than 1 is not supported (got length %d)", count) + } + + // See comment in encodeTPMLPCRSelection for details on this format. + var ts tpmsPCRSelection + if err := UnpackBuf(buf, &ts.Hash, &ts.Size); err != nil { + return sel, err + } + ts.PCRs = make([]byte, ts.Size) + if _, err := buf.Read(ts.PCRs); err != nil { + return sel, err + } + + sel.Hash = ts.Hash + for i := 0; i < int(ts.Size); i++ { + for j := 0; j < 8; j++ { + set := ts.PCRs[i] & byte(1<<byte(j)) + if set == 0 { + continue + } + sel.PCRs = append(sel.PCRs, 8*i+j) + } + } + return sel, nil +} + +type tpmsPCRSelection struct { + Hash Algorithm + Size byte + PCRs RawBytes +} + +// RawBytes is for Pack and RunCommand arguments that are already encoded. +// Compared to []byte, RawBytes will not be prepended with slice length during +// encoding. +type RawBytes []byte |