diff --git a/pr_comment.md b/pr_comment.md new file mode 100644 index 00000000..e69de29b diff --git a/scheme/tpm-enacttrust/store_handler.go b/scheme/tpm-enacttrust/store_handler.go index 09aaf5a9..fd15970b 100644 --- a/scheme/tpm-enacttrust/store_handler.go +++ b/scheme/tpm-enacttrust/store_handler.go @@ -43,7 +43,7 @@ func (s StoreHandler) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string strings.Join(EvidenceMediaTypes, ", "), token.MediaType, ) - return []string{""}, err + return nil, err } var decoded Token diff --git a/verification/api/challengeresponsesession.go b/verification/api/challengeresponsesession.go index 33e7035e..b80f01ae 100644 --- a/verification/api/challengeresponsesession.go +++ b/verification/api/challengeresponsesession.go @@ -6,6 +6,7 @@ package api import ( + "encoding/base64" "encoding/json" "fmt" "time" @@ -64,6 +65,32 @@ func (o *Status) UnmarshalJSON(b []byte) error { return o.FromString(s) } +// URLSafeNonce is a wrapper around []byte that marshals/unmarshals using URL-safe base64 +type URLSafeNonce []byte + +func (n URLSafeNonce) MarshalJSON() ([]byte, error) { + if n == nil { + return []byte("null"), nil + } + encoded := base64.URLEncoding.EncodeToString(n) + return json.Marshal(encoded) +} + +func (n *URLSafeNonce) UnmarshalJSON(data []byte) error { + var s string + if err := json.Unmarshal(data, &s); err != nil { + return err + } + + decoded, err := base64.URLEncoding.DecodeString(s) + if err != nil { + return err + } + + *n = URLSafeNonce(decoded) + return nil +} + type EvidenceBlob struct { Type string `json:"type"` Value []byte `json:"value"` @@ -72,7 +99,7 @@ type EvidenceBlob struct { type ChallengeResponseSession struct { id string Status Status `json:"status"` - Nonce []byte `json:"nonce"` + Nonce URLSafeNonce `json:"nonce"` Expiry time.Time `json:"expiry"` Accept []string `json:"accept"` Evidence *EvidenceBlob `json:"evidence,omitempty"` diff --git a/verification/api/handler.go b/verification/api/handler.go index a74d1ee5..7f11492f 100644 --- a/verification/api/handler.go +++ b/verification/api/handler.go @@ -168,7 +168,7 @@ func newSession(nonce []byte, supportedMediaTypes []string, ttl time.Duration) ( session := &ChallengeResponseSession{ id: id.String(), Status: StatusWaiting, // start in waiting status - Nonce: nonce, + Nonce: URLSafeNonce(nonce), Expiry: time.Now().Add(ttl), // RFC3339 format, with sub-second precision added if present Accept: supportedMediaTypes, } @@ -394,7 +394,7 @@ func (o *Handler) SubmitEvidence(c *gin.Context) { // reported if something in the verifier or the connection goes wrong. // Any problems with the evidence are expected to be reported via the // attestation result. - attestationResult, err := o.Verifier.ProcessEvidence(tenantID, session.Nonce, + attestationResult, err := o.Verifier.ProcessEvidence(tenantID, []byte(session.Nonce), evidence, mediaType) if err != nil { o.logger.Error(err) diff --git a/verification/api/handler_test.go b/verification/api/handler_test.go index 98815aba..fce1e4e7 100644 --- a/verification/api/handler_test.go +++ b/verification/api/handler_test.go @@ -45,7 +45,7 @@ var ( testJSONBody = `{ "k": "v" }` testSession = `{ "status": "waiting", - "nonce": "mVubqtg3Wa5GSrx3L/2B99cQU2bMQFVYUI9aTmDYi64=", + "nonce": "mVubqtg3Wa5GSrx3L_2B99cQU2bMQFVYUI9aTmDYi64=", "expiry": "2022-07-13T13:50:24.520525+01:00", "accept": [ "application/eat_cwt;profile=http://arm.com/psa/2.0.0", @@ -61,7 +61,7 @@ var ( }` testProcessingSession = `{ "status": "processing", - "nonce": "mVubqtg3Wa5GSrx3L/2B99cQU2bMQFVYUI9aTmDYi64=", + "nonce": "mVubqtg3Wa5GSrx3L_2B99cQU2bMQFVYUI9aTmDYi64=", "expiry": "2022-07-13T13:50:24.520525+01:00", "accept": [ "application/eat_cwt;profile=http://arm.com/psa/2.0.0", @@ -75,7 +75,7 @@ var ( }` testCompleteSession = `{ "status": "complete", - "nonce": "mVubqtg3Wa5GSrx3L/2B99cQU2bMQFVYUI9aTmDYi64=", + "nonce": "mVubqtg3Wa5GSrx3L_2B99cQU2bMQFVYUI9aTmDYi64=", "expiry": "2022-07-13T13:50:24.520525+01:00", "accept": [ "application/eat_cwt;profile=http://arm.com/psa/2.0.0", @@ -289,12 +289,42 @@ func TestHandler_NewChallengeResponse_NonceParameter(t *testing.T) { assert.Equal(t, expectedCode, w.Code) assert.Equal(t, expectedType, w.Result().Header.Get("Content-Type")) assert.Regexp(t, expectedLocationRE, w.Result().Header.Get("Location")) - assert.Equal(t, expectedNonce, body.Nonce) + assert.Equal(t, expectedNonce, []byte(body.Nonce)) assert.Nil(t, body.Evidence) assert.Nil(t, body.Result) assert.Equal(t, expectedSessionStatus, body.Status) } +func TestURLSafeNonce_EncodingFormat(t *testing.T) { + // Test that nonces with characters that would be URL-unsafe in standard base64 + // are properly encoded as URL-safe base64 + testNonce := []byte{0x99, 0x5b, 0x9b, 0xaa, 0xd8, 0x37, 0x59, 0xae, + 0x46, 0x4a, 0xbc, 0x77, 0x2f, 0xfd, 0x81, 0xf7, + 0xd7, 0x10, 0x53, 0x66, 0xcc, 0x40, 0x55, 0x58, + 0x50, 0x8f, 0x5a, 0x4e, 0x60, 0xd8, 0x8b, 0xae} + + urlSafeNonce := URLSafeNonce(testNonce) + jsonBytes, err := json.Marshal(urlSafeNonce) + require.NoError(t, err) + + jsonStr := string(jsonBytes) + t.Logf("Encoded nonce: %s", jsonStr) + + // Should not contain URL-unsafe characters '+' or '/' + assert.NotContains(t, jsonStr, "+", "Nonce should not contain '+' character") + assert.NotContains(t, jsonStr, "/", "Nonce should not contain '/' character") + + // Should contain URL-safe alternatives '_' and '-' instead + // Note: This specific test nonce should contain '_' character + assert.Contains(t, jsonStr, "_", "URL-safe nonce should contain '_' character") + + // Test round-trip: unmarshal and compare + var unmarshaled URLSafeNonce + err = json.Unmarshal(jsonBytes, &unmarshaled) + require.NoError(t, err) + assert.Equal(t, testNonce, []byte(unmarshaled), "Round-trip encoding should preserve nonce data") +} + func TestHandler_NewChallengeResponse_NonceSizeParameter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() diff --git a/vts/trustedservices/trustedservices_grpc.go b/vts/trustedservices/trustedservices_grpc.go index 57af5653..a5f2db7d 100644 --- a/vts/trustedservices/trustedservices_grpc.go +++ b/vts/trustedservices/trustedservices_grpc.go @@ -442,7 +442,6 @@ func (o *GRPC) GetAttestation( var multEndorsements []string for _, refvalID := range appraisal.EvidenceContext.ReferenceIds { - endorsements, err := o.EnStore.Get(refvalID) if err != nil && !errors.Is(err, kvstore.ErrKeyNotFound) { return o.finalize(appraisal, err)