Skip to content

Commit

Permalink
Merge pull request #15 from panwarsandeep/main
Browse files Browse the repository at this point in the history
Fix Get Report and Get Quote ioctl
  • Loading branch information
panwarsandeep authored Aug 17, 2023
2 parents 050bc72 + 4c57485 commit c16a1ca
Show file tree
Hide file tree
Showing 5 changed files with 37 additions and 205 deletions.
81 changes: 10 additions & 71 deletions client/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@
package client

import (
"bytes"
"encoding/binary"
"flag"
"fmt"

Expand All @@ -44,14 +42,8 @@ func UseDefaultTdxGuestDevice() bool {

// getReport requests for tdx report by making an ioctl call.
func getReport(d Device, reportData [64]byte) ([]uint8, error) {
tdxReportData := labi.TdxReportDataABI{
Data: reportData,
}
var tdxReport labi.TdxReportABI
tdxReportReq := labi.TdxReportReq{
ReportData: tdxReportData.Data,
TdReport: tdxReport.Data,
}
tdxReportReq := labi.TdxReportReq{}
copy(tdxReportReq.ReportData[:], reportData[:])
result, err := d.Ioctl(labi.IocTdxGetReport, &tdxReportReq)
if err != nil {
return nil, err
Expand All @@ -64,46 +56,17 @@ func getReport(d Device, reportData [64]byte) ([]uint8, error) {

// GetRawQuote call getReport for report and convert it to quote using an ioctl call.
func GetRawQuote(d Device, reportData [64]byte) ([]uint8, uint32, error) {
tdxReport := labi.TdxReportABI{
Data: [labi.TdReportSize]uint8{},
}
tdReport, err := getReport(d, reportData)
if err != nil {
return nil, 0, err
}
copy(tdxReport.Data[:labi.TdReportSize], tdReport[:labi.TdReportSize])
//get serialized quote request.
msgSize := uint32(labi.GetQuotesReqSize + labi.TdReportSize)
req := labi.SerializedGetQuoteReq{
Header: labi.MsgHeader{
MajorVersion: labi.MsgLibMajorVer,
MinorVersion: labi.MsgLibMinorVer,
MsgType: labi.GetQuoteReq,
Size: msgSize,
ErrorCode: 0,
},
IDListSize: 0,
ReportSize: labi.TdReportSize,
ReportIDList: tdxReport.Data,
}
reportIDSize := new(bytes.Buffer)
err = binary.Write(reportIDSize, binary.LittleEndian, msgSize)
if err != nil {
return nil, 0, err
}
reportID := new(bytes.Buffer)
err = binary.Write(reportID, binary.LittleEndian, req)
if err != nil {
return nil, 0, err
}
data := append(reportIDSize.Bytes(), reportID.Bytes()...)
tdxHdr := &labi.TdxQuoteHdr{
Status: 0,
Version: 1,
InLen: labi.HeaderSize + msgSize,
InLen: labi.TdReportSize,
OutLen: 0,
}
copy(tdxHdr.Data[:], data[0:])
copy(tdxHdr.Data[:], tdReport[:labi.TdReportSize])
tdxReq := labi.TdxQuoteReq{
Buffer: tdxHdr,
Length: labi.ReqBufSize,
Expand All @@ -115,43 +78,19 @@ func GetRawQuote(d Device, reportData [64]byte) ([]uint8, uint32, error) {
if result != uintptr(labi.TdxAttestSuccess) {
return nil, 0, fmt.Errorf("unable to get the quote")
}
if tdxHdr.Status != 0 || tdxHdr.OutLen <= labi.HeaderSize {
if tdxHdr.Status != 0 {
if labi.GetQuoteInFlight == tdxHdr.Status {
return nil, 0, fmt.Errorf("the device driver return busy")
} else if labi.GetQuoteServiceUnavailable == tdxHdr.Status {
return nil, 0, fmt.Errorf("request feature is not supported")
} else if tdxHdr.OutLen == 0 || tdxHdr.OutLen > labi.ReqBufSize {
return nil, 0, fmt.Errorf("invalid Quote size: %v. It must be > 0 and <= : %v", tdxHdr.OutLen, labi.ReqBufSize)
} else {
return nil, 0, fmt.Errorf("unexpected error")
return nil, 0, fmt.Errorf("unexpected error: %v", tdxHdr.Status)
}
}
inMsgSize := binary.LittleEndian.Uint32(tdxHdr.Data[0:])
if inMsgSize != tdxHdr.OutLen-labi.HeaderSize {
return nil, 0, fmt.Errorf("unexpected error")
}
// sanity check, the size shouldn't smaller than SerializedGetQuoteReq
if inMsgSize < labi.GetQuoteRespSize {
return nil, 0, fmt.Errorf("unexpected error")
}
resp := labi.SerializedGetQuoteResp{}
buff := bytes.NewReader(tdxHdr.Data[4:])
err = binary.Read(buff, binary.LittleEndian, &resp)
if err != nil {
return nil, 0, err
}
// Only major version is checked, minor change is deemed as compatible.
if resp.Header.MajorVersion != labi.MsgLibMajorVer {
return nil, 0, fmt.Errorf("unrecognized version of serialized data")
}
if resp.Header.MsgType != labi.GetQuoteResp {
return nil, 0, fmt.Errorf("invalid message type found")
}
if resp.Header.Size != inMsgSize {
return nil, 0, fmt.Errorf("invalid message size found")
}
if resp.Header.ErrorCode == labi.TdxAttestSuccess {
return resp.IDQuote[:resp.QuoteSize], resp.QuoteSize, nil
}
return []uint8{}, 0, nil

return tdxHdr.Data[:tdxHdr.OutLen], tdxHdr.OutLen, nil
}

// GetQuote call GetRawQuote to get the quote in byte array and convert it into proto.
Expand Down
8 changes: 4 additions & 4 deletions client/client_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@ package client
import (
"fmt"

"unsafe"

labi "github.com/google/go-tdx-guest/client/linuxabi"
"golang.org/x/sys/unix"
)

// defaultTdxGuestDevicePath is the platform's usual device path to the TDX guest.
const defaultTdxGuestDevicePath = "/dev/tdx-guest"
const defaultTdxGuestDevicePath = "/dev/tdx_guest"

// LinuxDevice implements the Device interface with Linux ioctls.
type LinuxDevice struct {
Expand Down Expand Up @@ -87,9 +89,7 @@ func (d *LinuxDevice) Ioctl(command uintptr, req any) (uintptr, error) {
}
return result, nil
case *labi.TdxReportReq:
abi := sreq.ABI()
result, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(d.fd), command, uintptr(abi.Pointer()))
abi.Finish(sreq)
result, _, errno := unix.Syscall(unix.SYS_IOCTL, uintptr(d.fd), command, uintptr(unsafe.Pointer(req.(*labi.TdxReportReq))))
if errno != 0 {
return 0, errno
}
Expand Down
107 changes: 10 additions & 97 deletions client/linuxabi/linux_abi.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,13 @@ const (
iocWrite = 1
iocRead = 2
// Linux /dev/tdx-guest ioctl interface
iocTypeTdxGuestReq = 'T'
iocTdxWithoutNr = ((iocWrite | iocRead) << iocDirshift) |
(iocTypeTdxGuestReq << iocTypeshift) |
(64 << iocSizeshift)
// IocTdxGetReport is the ioctl command for getting an attestation report
IocTdxGetReport = iocTdxWithoutNr | (0x1 << iocNrshift)
// IocTdxGetQuote is the ioctl command for getting an attestation quote
IocTdxGetQuote = ((iocRead) << iocDirshift) |
(iocTypeTdxGuestReq << iocTypeshift) | (0x2 << iocNrshift) |
(64 << iocSizeshift)
iocTypeTdxGuestReq = 'T'
iocTdxWithoutNrWithoutSize = ((iocWrite | iocRead) << iocDirshift) |
(iocTypeTdxGuestReq << iocTypeshift)
// IocTdxGetReport is the ioctl command for getting an attestation report.
IocTdxGetReport = iocTdxWithoutNrWithoutSize | (unsafe.Sizeof(TdxReportReq{}) << iocSizeshift) | (0x1 << iocNrshift)
// IocTdxGetQuote is the ioctl command for getting an attestation quote.
IocTdxGetQuote = iocTdxWithoutNrWithoutSize | (unsafe.Sizeof(TdxQuoteReqABI{}) << iocSizeshift) | (0x2 << iocNrshift)
// TdReportDataSize is a constant for TDX ReportData size
TdReportDataSize = 64
// TdReportSize is a constant for TDX Report size
Expand All @@ -57,20 +54,6 @@ const (
GetQuoteReq = 0
// GetQuoteResp is a constant for report response
GetQuoteResp = 1
// MsgLibMajorVer is a constant for major version for header
MsgLibMajorVer = 1
// MsgLibMinorVer is a constant for minor version for header
MsgLibMinorVer = 0
// GetQuotesReqSize is a constant for Quote Request Msg
GetQuotesReqSize = 24
// GetQuoteRespSize is a constant for Quote Response Msg
GetQuoteRespSize = 24
// TdxQuoteHdrSize is a constant for Quote Header size
TdxQuoteHdrSize = 24
// TdQuoteBufSize is a constant denotes buffer size quote request
TdQuoteBufSize = ReqBufSize - GetQuotesReqSize
// TdIDQuoteSize is a constant denotes maximum size of array containing Quote
TdIDQuoteSize = TdQuoteBufSize - HeaderSize - TdxQuoteHdrSize
)

// EsResult is the status code type for Linux's GHCB communication results.
Expand All @@ -96,75 +79,13 @@ const (
TdxAttestErrorUnexpected = 0x0001
)

// TdxReportDataABI is Linux's tdx-guest abi for ReportData
type TdxReportDataABI struct {
// Data is the reportData of 64 bytes
Data [TdReportDataSize]uint8
}

// ABI returns the object itself.
func (r *TdxReportDataABI) ABI() BinaryConversion { return r }

// Pointer returns a pointer to the object itself.
func (r *TdxReportDataABI) Pointer() unsafe.Pointer { return unsafe.Pointer(r) }

// Finish is a no-op.
func (r *TdxReportDataABI) Finish(BinaryConvertible) error {
return nil
}

// TdxReportABI is Linux's tdx-guest abi for report response
type TdxReportABI struct {
// Data is the report response data
Data [TdReportSize]uint8
}

// ABI returns the object itself.
func (r *TdxReportABI) ABI() BinaryConversion { return r }

// Pointer returns a pointer to the object itself.
func (r *TdxReportABI) Pointer() unsafe.Pointer { return unsafe.Pointer(r) }

// Finish is a no-op.
func (r *TdxReportABI) Finish(BinaryConvertible) error {
return nil
}

// TdxReportReqABI is Linux's tdx-guest ABI for TDX Report
type TdxReportReqABI struct {
/* Report data of 64 bytes */
ReportData unsafe.Pointer
/* Actual TD Report Data */
TdReport unsafe.Pointer
}

// TdxReportReq is Linux's tdx-guest ABI for TDX Report. The
// types here enhance runtime safety when using Ioctl as an interface.
type TdxReportReq struct {
/* Report data of 64 bytes */
ReportData [TdReportDataSize]uint8
ReportData [TdReportDataSize]byte
/* Actual TD Report Data */
TdReport [TdReportSize]uint8
}

// ABI returns the object itself.
func (r *TdxReportReq) ABI() BinaryConversion {
return &TdxReportReqABI{
ReportData: unsafe.Pointer(&r.ReportData[0]),
TdReport: unsafe.Pointer(&r.TdReport[0]),
}
}

// Pointer returns a pointer to the object itself.
func (r *TdxReportReqABI) Pointer() unsafe.Pointer { return unsafe.Pointer(r) }

// Finish writes back the changed value.
func (r *TdxReportReqABI) Finish(b BinaryConvertible) error {
_, ok := b.(*TdxReportReq)
if !ok {
return fmt.Errorf("argument is %v. Expects a *TdxReportReq", reflect.TypeOf(b))
}
return nil
TdReport [TdReportSize]byte
}

// MsgHeader is used to add header field to serialized request and response message.
Expand All @@ -184,14 +105,6 @@ type SerializedGetQuoteReq struct {
ReportIDList [TdReportSize]uint8 // report followed by id list - [TODO revisit if attestation key ID is included]
}

// SerializedGetQuoteResp is used to serialized the response message.
type SerializedGetQuoteResp struct {
Header MsgHeader // header.type = GET_QUOTE_RESP
SelectedIDSize uint32 // can be 0 in case only one id is sent in request
QuoteSize uint32 // length of quote_data, in byte
IDQuote [TdIDQuoteSize]uint8 // selected id followed by quote -[TODO revisit if attestation key ID is included]
}

// TdxQuoteHdr is Linux's tdx-guest ABI for quote header
type TdxQuoteHdr struct {
/* Quote version, filled by TD */
Expand All @@ -203,7 +116,7 @@ type TdxQuoteHdr struct {
/* Length of Quote, filled by VMM */
OutLen uint32
/* Actual Quote data or TDREPORT on input */
Data [TdQuoteBufSize]byte
Data [ReqBufSize]byte
}

// ABI returns the object itself.
Expand Down
25 changes: 6 additions & 19 deletions testing/mocks.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
package testing

import (
"bytes"
"encoding/binary"
"fmt"
"strings"

Expand All @@ -26,13 +24,13 @@ import (

// GetReportResponse represents a mocked response to a command request.
type GetReportResponse struct {
Resp labi.TdxReportABI
Resp labi.TdxReportReq
EsResult labi.EsResult
}

// GetQuoteResponse represents a mocked response to a command request.
type GetQuoteResponse struct {
Resp labi.SerializedGetQuoteResp
Resp labi.TdxQuoteHdr
EsResult labi.EsResult
}

Expand Down Expand Up @@ -79,13 +77,13 @@ func (d *Device) getReport(req *labi.TdxReportReq) (uintptr, error) {
return 0, fmt.Errorf("test error: incorrect response for %v", tdReportRespI)
}
esResult := uintptr(tdReportResp.EsResult)
req.TdReport = tdReportResp.Resp.Data
req.TdReport = tdReportResp.Resp.TdReport
return esResult, nil
}

func (d *Device) getQuote(req *labi.TdxQuoteHdr) (uintptr, error) {
var report [labi.TdReportSize]byte
copy(report[:], req.Data[labi.GetQuotesReqSize+labi.HeaderSize:])
copy(report[:], req.Data[:labi.TdReportSize])
quoteRespI, ok := d.quoteResponse[report]
if !ok {
return 0, fmt.Errorf("test error: no response for %v", report)
Expand All @@ -96,19 +94,8 @@ func (d *Device) getQuote(req *labi.TdxQuoteHdr) (uintptr, error) {
return 0, fmt.Errorf("test error: incorrect response for %v", quoteRespI)
}
esResult := uintptr(quoteResp.EsResult)

msgSize := uint32(quoteResp.Resp.Header.Size)
respSize := new(bytes.Buffer)
if err := binary.Write(respSize, binary.LittleEndian, msgSize); err != nil {
return 0, err
}
resp := new(bytes.Buffer)
if err := binary.Write(resp, binary.LittleEndian, quoteResp.Resp); err != nil {
return 0, err
}
data := append(respSize.Bytes(), resp.Bytes()...)
req.OutLen = quoteResp.Resp.QuoteSize + labi.HeaderSize + labi.GetQuoteRespSize
copy(req.Data[:], data[:])
copy(req.Data[:], quoteResp.Resp.Data[:])
req.OutLen = quoteResp.Resp.OutLen
return esResult, nil
}

Expand Down
21 changes: 7 additions & 14 deletions testing/test_cases.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,25 +109,18 @@ func TcDevice(tcs []TestCase) (*Device, error) {
quoteResponses := map[[labi.TdReportSize]byte]any{}
for _, tc := range tcs {
reportResponses[tc.Input] = &GetReportResponse{
Resp: labi.TdxReportABI{
Data: tc.Report,
Resp: labi.TdxReportReq{
TdReport: tc.Report,
},
EsResult: tc.EsResult,
}
var idQuote [labi.TdIDQuoteSize]byte
var idQuote [labi.ReqBufSize]byte
copy(idQuote[:], tc.Quote)
quoteResponses[tc.Report] = &GetQuoteResponse{
Resp: labi.SerializedGetQuoteResp{
Header: labi.MsgHeader{
MajorVersion: labi.MsgLibMajorVer,
MinorVersion: labi.MsgLibMinorVer,
MsgType: labi.GetQuoteResp,
Size: 4998,
ErrorCode: 0,
},
QuoteSize: 4974,
SelectedIDSize: 0,
IDQuote: idQuote,
Resp: labi.TdxQuoteHdr{
Status: labi.GetQuoteSuccess,
OutLen: uint32(len(tc.Quote)),
Data: idQuote,
},
EsResult: tc.EsResult,
}
Expand Down

0 comments on commit c16a1ca

Please sign in to comment.