Skip to content

Commit 04f8b0c

Browse files
committed
Return meaningful EAT in chares
Endpoint chares now gathers the output of GetEvidence() from each sub-attesters, combines them into a CMW collection, and wrap it as an EAT in its response. If there are multiple supported format available from a sub-attesters, ratsd core picks the first available format from GetSupportedFormats() Signed-off-by: Ian Chin Wang <[email protected]>
1 parent 39403e3 commit 04f8b0c

File tree

10 files changed

+290
-13
lines changed

10 files changed

+290
-13
lines changed

.github/workflows/ci.yml

+3
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ jobs:
3333
- name: Install protoc-gen-go-grpc
3434
run: go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@latest
3535

36+
- name: Install mockgen
37+
run: go install github.com/golang/mock/[email protected]
38+
3639
- name: Generate protobufs and ratsd server
3740
run: go generate ./...
3841

api/mocks/imanager.go

+92
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/mocks/ipluggable.go

+77
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/server.go

+78-4
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,16 @@
33
package api
44

55
import (
6+
"encoding/base64"
67
"encoding/json"
78
"fmt"
89
"io"
910
"net/http"
1011

1112
"github.com/moogar0880/problems"
13+
"github.com/veraison/cmw"
14+
"github.com/veraison/ratsd/plugin"
15+
"github.com/veraison/ratsd/proto/compositor"
1216
"go.uber.org/zap"
1317
)
1418

@@ -19,12 +23,14 @@ const (
1923
)
2024

2125
type Server struct {
22-
logger *zap.SugaredLogger
26+
logger *zap.SugaredLogger
27+
manager plugin.IManager
2328
}
2429

25-
func NewServer(logger *zap.SugaredLogger) *Server {
30+
func NewServer(logger *zap.SugaredLogger, manager plugin.IManager) *Server {
2631
return &Server{
27-
logger: logger,
32+
logger: logger,
33+
manager: manager,
2834
}
2935
}
3036

@@ -90,8 +96,76 @@ func (s *Server) RatsdChares(w http.ResponseWriter, r *http.Request, param Ratsd
9096
return
9197
}
9298

99+
nonce, err := base64.RawURLEncoding.DecodeString(requestData.Nonce)
100+
if err != nil {
101+
errMsg := fmt.Sprintf("fail to decode nonce from the request: %s", err.Error())
102+
p := &problems.DefaultProblem{
103+
Type: string(TagGithubCom2024VeraisonratsdErrorInvalidrequest),
104+
Title: string(InvalidRequest),
105+
Detail: errMsg,
106+
Status: http.StatusBadRequest,
107+
}
108+
s.reportProblem(w, p)
109+
return
110+
}
93111
s.logger.Info("request nonce: ", requestData.Nonce)
112+
s.logger.Info("request media type: ", *(param.Accept))
113+
114+
// Use a map until we finalize ratsd output format
115+
eat := make(map[string]interface{})
116+
collection := make(map[string]string)
117+
eat["eat_profile"] = TagGithubCom2024Veraisonratsd
118+
eat["eat_nonce"] = requestData.Nonce
119+
eat["cmw"] = collection
120+
121+
for _, pn := range s.manager.GetPluginList() {
122+
attester, err := s.manager.LookupByName(pn)
123+
if err != nil {
124+
errMsg := fmt.Sprintf(
125+
"failed to get handle from %s: %s", pn, err.Error())
126+
p := problems.NewDetailedProblem(http.StatusInternalServerError, errMsg)
127+
s.reportProblem(w, p)
128+
return
129+
}
130+
131+
formatOut := attester.GetSupportedFormats()
132+
if !formatOut.Status.Result || len(formatOut.Formats) == 0 {
133+
errMsg := fmt.Sprintf("no supported formats from attester %s: %s ",
134+
pn, formatOut.Status.Error)
135+
s.logger.Info(errMsg)
136+
continue
137+
}
138+
139+
s.logger.Info("output content type: ", formatOut.Formats[0].ContentType)
140+
in := &compositor.EvidenceIn{
141+
ContentType: formatOut.Formats[0].ContentType,
142+
Nonce: nonce,
143+
}
144+
145+
out := attester.GetEvidence(in)
146+
if !out.Status.Result {
147+
errMsg := fmt.Sprintf(
148+
"failed to get attestation report from %s: %s ", pn, out.Status.Error)
149+
p := problems.NewDetailedProblem(http.StatusInternalServerError, errMsg)
150+
s.reportProblem(w, p)
151+
return
152+
}
153+
154+
c := &cmw.CMW{}
155+
c.SetMediaType(in.ContentType)
156+
c.SetValue(out.Evidence)
157+
serialized, err := c.Serialize(cmw.JSONArray)
158+
if err != nil {
159+
errMsg := fmt.Sprintf(
160+
"failed to serialize CMW for %s: %s ", pn, err.Error())
161+
p := problems.NewDetailedProblem(http.StatusInternalServerError, errMsg)
162+
s.reportProblem(w, p)
163+
return
164+
}
165+
collection[pn] = string(serialized)
166+
}
167+
94168
w.Header().Set("Content-Type", respCt)
95169
w.WriteHeader(http.StatusOK)
96-
w.Write([]byte("hello from ratsd!"))
170+
json.NewEncoder(w).Encode(eat)
97171
}

api/server_test.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,16 @@ import (
1010
"strings"
1111
"testing"
1212

13+
"github.com/golang/mock/gomock"
1314
"github.com/moogar0880/problems"
1415
"github.com/stretchr/testify/assert"
16+
mock_deps "github.com/veraison/ratsd/api/mocks"
1517
"github.com/veraison/services/log"
1618
)
1719

1820
const (
19-
jsonType = "application/json"
21+
jsonType = "application/json"
22+
validNonce = "TUlEQk5IMjhpaW9pc2pQeXh4eHh4eHh4eHh4eHh4eHg"
2023
)
2124

2225
func TestRatsdChares_missing_auth_header(t *testing.T) {
@@ -129,25 +132,39 @@ func TestRatsdChares_missing_nonce(t *testing.T) {
129132
assert.Equal(t, expectedBody, &body)
130133
}
131134

132-
func TestRatsdChares_valid_request(t *testing.T) {
135+
func TestRatsdChares_valid_request_no_available_attester(t *testing.T) {
136+
ctrl := gomock.NewController(t)
137+
defer ctrl.Finish()
138+
133139
var params RatsdCharesParams
134140

135141
param := fmt.Sprintf(`application/eat+jwt; eat_profile=%q`, TagGithubCom2024Veraisonratsd)
136142
params.Accept = &param
137143
logger := log.Named("test")
138-
s := &Server{logger: logger}
144+
145+
pluginList := []string{}
146+
dm := mock_deps.NewMockIManager(ctrl)
147+
dm.EXPECT().GetPluginList().Return(pluginList)
148+
149+
s := NewServer(logger, dm)
139150
w := httptest.NewRecorder()
140-
rb := strings.NewReader("{\"nonce\": \"MIDBNH28iioisjPy\"}")
151+
rs := fmt.Sprintf("{\"nonce\": \"%s\"}", validNonce)
152+
rb := strings.NewReader(rs)
141153
r, _ := http.NewRequest(http.MethodPost, "/ratsd/chares", rb)
142154
r.Header.Add("Authorization", ExpectedAuth)
143155
r.Header.Add("Content-Type", ApplicationvndVeraisonCharesJson)
144156
s.RatsdChares(w, r, params)
145157

146158
expectedCode := http.StatusOK
147159
expectedType := param
148-
expectedBody := "hello from ratsd!"
149160

150161
assert.Equal(t, expectedCode, w.Code)
151162
assert.Equal(t, expectedType, w.Result().Header.Get("Content-Type"))
152-
assert.Equal(t, expectedBody, w.Body.String())
163+
164+
var out map[string]interface{}
165+
json.Unmarshal(w.Body.Bytes(), &out)
166+
assert.Equal(t, string(TagGithubCom2024Veraisonratsd), out["eat_profile"])
167+
assert.Equal(t, validNonce, out["eat_nonce"])
168+
assert.Contains(t, out, "cmw")
169+
assert.Empty(t, out["cmw"])
153170
}

cmd/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func main() {
7272

7373
log.Info("Loaded sub-attesters:", pluginManager.GetPluginList())
7474

75-
svr := api.NewServer(log.Named("api"))
75+
svr := api.NewServer(log.Named("api"), pluginManager)
7676
r := http.NewServeMux()
7777
h := api.HandlerFromMux(svr, r)
7878

go.mod

+2
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ go 1.24.1
55
require (
66
github.com/fxamacker/cbor/v2 v2.5.0
77
github.com/getkin/kin-openapi v0.128.0
8+
github.com/golang/mock v1.6.0
89
github.com/google/go-configfs-tsm v0.3.2
910
github.com/hashicorp/go-plugin v1.4.4
1011
github.com/moogar0880/problems v0.1.1
1112
github.com/oapi-codegen/runtime v1.1.1
1213
github.com/stretchr/testify v1.9.0
14+
github.com/veraison/cmw v0.1.1
1315
github.com/veraison/services v0.0.2501
1416
go.uber.org/zap v1.23.0
1517
google.golang.org/grpc v1.64.0

0 commit comments

Comments
 (0)