Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tsm report token #38

Merged
merged 2 commits into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,9 @@ jobs:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '1.22'
go-version: '1.24.1'

- name: Install Protoc
uses: arduino/setup-protoc@v3
Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ build:
.PHONY: test
test:
go test -v github.com/veraison/ratsd/api
go test -v github.com/veraison/ratsd/tokens

.PHONY: clean
clean:
Expand Down
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
module github.com/veraison/ratsd

go 1.22.7
go 1.24.1

require (
github.com/fxamacker/cbor/v2 v2.5.0
github.com/getkin/kin-openapi v0.128.0
github.com/hashicorp/go-plugin v1.4.4
github.com/moogar0880/problems v0.1.1
Expand Down Expand Up @@ -69,6 +70,7 @@ require (
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/vmware-labs/yaml-jsonpath v0.3.2 // indirect
github.com/x448/float16 v0.8.4 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/arch v0.7.0 // indirect
Expand All @@ -83,3 +85,5 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

tool github.com/oapi-codegen/oapi-codegen/v2/cmd/oapi-codegen
12 changes: 12 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMo
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fxamacker/cbor/v2 v2.5.0 h1:oHsG0V/Q6E/wqTS2O1Cozzsy69nqCiguo5Q1a1ADivE=
github.com/fxamacker/cbor/v2 v2.5.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/getkin/kin-openapi v0.128.0 h1:jqq3D9vC9pPq1dGcOCv7yOp1DaEe7c/T1vzcLbITSp4=
Expand Down Expand Up @@ -246,6 +248,7 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwd
github.com/moogar0880/problems v0.1.1 h1:bktLhq8NDG/czU2ZziYNigBFksx13RaYe5AVdNmHDT4=
github.com/moogar0880/problems v0.1.1/go.mod h1:5Dxrk2sD7BfBAgnOzQ1yaTiuCYdGPUh49L8Vhfky62c=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1 h1:ykgG34472DWey7TSjd8vIfNykXgjOgYJZoQbKfEeY/Q=
github.com/oapi-codegen/oapi-codegen/v2 v2.4.1/go.mod h1:N5+lY1tiTDV3V1BeHtOxeWXHoPVeApvsvjJqegfoaz8=
Expand All @@ -256,13 +259,16 @@ github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQ
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.2/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=
github.com/onsi/gomega v1.27.6/go.mod h1:PIQNjfQwkP3aQAH7lf7j87O/5FiNr+ZR8+ipb+qQlhg=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo=
Expand All @@ -280,6 +286,7 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
github.com/speakeasy-api/openapi-overlay v0.9.0 h1:Wrz6NO02cNlLzx1fB093lBlYxSI54VRhy1aSutx0PQg=
github.com/speakeasy-api/openapi-overlay v0.9.0/go.mod h1:f5FloQrHA7MsxYg9djzMD5h6dxrHjVVByWKh7an8TRc=
Expand Down Expand Up @@ -319,6 +326,8 @@ github.com/veraison/services v0.0.2501 h1:tK4a92RvceURIlT0pEkz3fU0TpaZ9jbcWqkPRk
github.com/veraison/services v0.0.2501/go.mod h1:snBdw93xJ4j9sSrYueT3QlAB8oWGePwkq7H+kYf/lLc=
github.com/vmware-labs/yaml-jsonpath v0.3.2 h1:/5QKeCBGdsInyDCyVNLbXyilb61MXGi9NP674f9Hobk=
github.com/vmware-labs/yaml-jsonpath v0.3.2/go.mod h1:U6whw1z03QyqgWdgXxvVnQ90zN1BWz5V+51Ewf8k+rQ=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
Expand Down Expand Up @@ -440,6 +449,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -665,6 +676,7 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
Expand Down
113 changes: 113 additions & 0 deletions tokens/tsm-report.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
package tokens

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"

"github.com/fxamacker/cbor/v2"
)

const (
TSMReportMediaTypeCBOR = "application/vnd.veraison.tsm-report+cbor"
TSMReportMediaTypeJSON = "application/vnd.veraison.tsm-report+json"
)

// BinaryString is base64url (§5 of RFC4648) without padding.
type BinaryString []byte

// MarshalJSON encodes BinaryString as JSON
func (o BinaryString) MarshalJSON() ([]byte, error) {
return json.Marshal(
base64.RawURLEncoding.EncodeToString(o),
)
}

// UnmarshalJSON decodes BinaryString from JSON
func (o *BinaryString) UnmarshalJSON(b []byte) error {
var s string

if err := json.Unmarshal(b, &s); err != nil {
return err
}

decoded, err := base64.RawURLEncoding.DecodeString(s)
if err != nil {
return err
}

*o = decoded
return nil
}

// TSMReport represents the TSM report generated by Linux
// see docs/tsm-report.cddl for definition
type TSMReport struct {
AuxBlob BinaryString `json:"auxblob,omitempty"`
OutBlob BinaryString `json:"outblob"`
Provider string `json:"provider"`
ManifestBlob BinaryString `json:"manifestblob,omitempty"`
ServiceProvider *string `json:"service_provider,omitempty"`
}

// Valid checks if the TSMReport is populated correctly
func (t *TSMReport) Valid() error {
if len(t.OutBlob) == 0 {
return errors.New(`missing mandatory field "outblob"`)
}

if t.Provider == "" {
return errors.New(`missing mandatory field "provider"`)
}

if len(t.ManifestBlob) > 0 && (t.ServiceProvider == nil || len(*t.ServiceProvider) == 0) {
return errors.New(`stray field "manifestblob"`)
}

return nil
}

// ToJSON encodes TSMReport as JSON
func (t *TSMReport) ToJSON() ([]byte, error) {
if err := t.Valid(); err != nil {
return nil, fmt.Errorf("JSON encoding failed: %w", err)
}

return json.Marshal(t)
}

// FromJSON decodes TSMReport from JSON
func (t *TSMReport) FromJSON(data []byte) error {
if err := json.Unmarshal(data, t); err != nil {
return fmt.Errorf("JSON decoding failed: %w", err)
}

if err := t.Valid(); err != nil {
return fmt.Errorf("JSON decoding failed: %w", err)
}

return nil
}

// ToCBOR encodes TSMReport as CBOR
func (t *TSMReport) ToCBOR() ([]byte, error) {
if err := t.Valid(); err != nil {
return nil, fmt.Errorf("CBOR encoding failed: %w", err)
}

return cbor.Marshal(t)
}

// FromCBOR decodes TSMReport from CBOR
func (t *TSMReport) FromCBOR(data []byte) error {
if err := cbor.Unmarshal(data, t); err != nil {
return fmt.Errorf("CBOR decoding failed: %w", err)
}

if err := t.Valid(); err != nil {
return fmt.Errorf("CBOR decoding failed: %w", err)
}

return nil
}
79 changes: 79 additions & 0 deletions tokens/tsm-report_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright 2025 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0
package tokens

import (
"github.com/stretchr/testify/assert"
"reflect"
"testing"
)

var provider = "sev_guest"
var serviceProvider = "svsm"
var outblob = []byte{117, 24, 74, 133, 21, 198, 189, 177, 81, 18, 129, 84}
var auxblob = []byte{120, 112, 34, 102, 228, 162, 111, 217, 232, 169, 167, 213, 197, 209, 94, 121, 161, 231, 170, 64}
var manifestblob = []byte{170, 13, 189, 115, 132, 249, 168, 13, 253, 229, 76, 198}

func Test_TSMReport_Valid_Pass(t *testing.T) {
report := &TSMReport{
Provider: provider,
OutBlob: outblob,
AuxBlob: auxblob,
ServiceProvider: &serviceProvider,
ManifestBlob: manifestblob,
}

assert.NoError(t, report.Valid())
}

func Test_TSMReport_Valid_Fail_ManifestBlob(t *testing.T) {
report := &TSMReport{
Provider: provider,
OutBlob: outblob,
AuxBlob: auxblob,
ManifestBlob: manifestblob,
}

assert.EqualError(t, report.Valid(), `stray field "manifestblob"`)
}

func Test_TSMReport_Valid_Fail_MandatoryField(t *testing.T) {
report := &TSMReport{
OutBlob: outblob,
AuxBlob: auxblob,
}

assert.EqualError(t, report.Valid(), `missing mandatory field "provider"`)
}

func Test_TSMReport_JSON_SerDes_Pass(t *testing.T) {
report := &TSMReport{
Provider: provider,
OutBlob: outblob,
AuxBlob: auxblob,
}

encodedJson, err := report.ToJSON()
assert.NoError(t, err)

decodedReport := &TSMReport{}
assert.NoError(t, decodedReport.FromJSON(encodedJson))

assert.True(t, reflect.DeepEqual(report, decodedReport))
}

func Test_TSMReport_CBOR_SerDes_Pass(t *testing.T) {
report := &TSMReport{
Provider: provider,
OutBlob: outblob,
AuxBlob: auxblob,
}

encodedCbor, err := report.ToCBOR()
assert.NoError(t, err)

decodedReport := &TSMReport{}
assert.NoError(t, decodedReport.FromCBOR(encodedCbor))

assert.True(t, reflect.DeepEqual(report, decodedReport))
}