Skip to content
Open
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 handler/store_rpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -241,13 +241,13 @@ func (s *StoreRPCClient) GetTrustAnchorIDs(token *proto.AttestationToken) ([]str

data, err = json.Marshal(token)
if err != nil {
return []string{""}, fmt.Errorf("marshaling token: %w", err)
return nil, fmt.Errorf("marshaling token: %w", err)
}

err = s.client.Call("Plugin.GetTrustAnchorIDs", data, &resp)
if err != nil {
err = ParseError(err)
return []string{""}, fmt.Errorf("Plugin.GetTrustAnchorIDs RPC call failed: %w", err) // nolint
return nil, fmt.Errorf("Plugin.GetTrustAnchorIDs RPC call failed: %w", err) // nolint
}

return resp, nil
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/data/claims/cca.good.json
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
"cca-platform-hash-algo-id": "sha-256"
},
"cca-realm-delegated-token": {
"cca-realm-challenge": "byTWuWNaLIu/WOkIuU4Ewb+zroDN6+gyQkV4SZ/jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-challenge": "byTWuWNaLIu_WOkIuU4Ewb-zroDN6-gyQkV4SZ_jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-personalization-value": "QURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBRA==",
"cca-realm-initial-measurement": "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==",
"cca-realm-extensible-measurements": [
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/data/results/cca.end-to-end.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"storage-opaque": 0
},
"ear.veraison.annotated-evidence": {
"cca-realm-challenge": "byTWuWNaLIu/WOkIuU4Ewb+zroDN6+gyQkV4SZ/jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-challenge": "byTWuWNaLIu_WOkIuU4Ewb-zroDN6-gyQkV4SZ_jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-extensible-measurements": [
"Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==",
"Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==",
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/data/results/cca.good.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@
"storage-opaque": 0
},
"ear.veraison.annotated-evidence": {
"cca-realm-challenge": "byTWuWNaLIu/WOkIuU4Ewb+zroDN6+gyQkV4SZ/jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-challenge": "byTWuWNaLIu_WOkIuU4Ewb-zroDN6-gyQkV4SZ_jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-extensible-measurements": [
"Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==",
"Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==",
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/data/results/cca.verify-challenge.json
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
"storage-opaque": 0
},
"ear.veraison.annotated-evidence": {
"cca-realm-challenge": "byTWuWNaLIu/WOkIuU4Ewb+zroDN6+gyQkV4SZ/jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-challenge": "byTWuWNaLIu_WOkIuU4Ewb-zroDN6-gyQkV4SZ_jF2Hn9eHYvOASGET1Sr36UobaiPU6ZXsVM1yTlrQyklS8XA==",
"cca-realm-extensible-measurements": [
"Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==",
"Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==",
Expand Down
14 changes: 12 additions & 2 deletions integration-tests/docker/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,18 @@ RUN apt-get update \
&& rm -rf /var/lib/apt/lists/* /var/tmp/* /tmp/* && \
gem install cbor-diag

RUN userdel -f $(cat /etc/passwd | awk -F: "\$3 == ${TESTER_UID}" | cut -d: -f1); \
groupdel -f $(cat /etc/group | awk -F: "\$3 == ${TESTER_GID}" | cut -d: -f1); \
# Note: unfortunately this does not get packaged as part of the distro (so
# cannot be installed with apt), and the upstream only provide an amd64 deb, so
# this will not work on arm64 platforms.
RUN wget https://dl.step.sm/gh-release/cli/docs-cli-install/v0.23.1/step-cli_0.23.1_amd64.deb && \
dpkg -i step-cli_0.23.1_amd64.deb; \
rm step-cli_0.23.1_amd64.deb


RUN user_to_del=$(awk -F: "\$3 == ${TESTER_UID} {print \$1}" /etc/passwd) && \
[ -n "$user_to_del" ] && userdel -f "$user_to_del" || true && \
group_to_del=$(awk -F: "\$3 == ${TESTER_GID} {print \$1}" /etc/group) && \
[ -n "$group_to_del" ] && groupdel -f "$group_to_del" || true && \
groupadd -g ${TESTER_GID} tavern && \
groupadd -g 616 veraison && \
useradd -m -u ${TESTER_UID} -g tavern -G veraison \
Expand Down
1 change: 1 addition & 0 deletions integration-tests/utils/checkers.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def compare_to_expected_result(response, expected, verifier_key):
print("Key exists in the dictionary.")
except KeyError:
print(f"Key {key} does not exist in the dictionary.")
raise

assert decoded_claims["ear.status"] == expected_claims["ear.status"]
print(f"Evaluating Submod with SubModName {key}")
Expand Down
6 changes: 3 additions & 3 deletions integration-tests/utils/generators.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,18 +128,18 @@ def generate_evidence(scheme, evidence, nonce, signing, outname):

if scheme == 'psa' and nonce:
claims_file = f'{GENDIR}/claims/{scheme}.{evidence}.json'
# Use nonce directly as URL-safe base64 to match verification API
update_json(
f'data/claims/{scheme}.{evidence}.json',
{f'{scheme}-nonce': nonce},
claims_file,
)
elif scheme == 'cca' and nonce:
claims_file = f'{GENDIR}/claims/{scheme}.{evidence}.json'
# convert nonce from base64url to base64
translated_nonce = nonce.replace('-', '+').replace('_', '/')
# Use nonce directly as URL-safe base64 to match verification API
update_json(
f'data/claims/{scheme}.{evidence}.json',
{'cca-realm-delegated-token': {f'cca-realm-challenge': translated_nonce}},
{'cca-realm-delegated-token': {f'cca-realm-challenge': nonce}},
claims_file,
)
else:
Expand Down
2 changes: 1 addition & 1 deletion log/hclogger.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022-2023 Contributors to the Veraison project.
// Copyright 2022-2025 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0
package log

Expand Down
6 changes: 3 additions & 3 deletions scheme/arm-cca/store_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,16 +48,16 @@ func (s StoreHandler) SynthKeysFromTrustAnchor(tenantID string, ta *handler.Endo
func (s StoreHandler) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string, error) {
evidence, err := ccatoken.DecodeAndValidateEvidenceFromCBOR(token.Data)
if err != nil {
return []string{""}, handler.BadEvidence(err)
return nil, handler.BadEvidence(err)
}

claims := evidence.PlatformClaims
if err != nil {
return []string{""}, err
return nil, err
}
taID, err := arm.GetTrustAnchorID(SchemeName, token.TenantId, claims)
if err != nil {
return []string{""}, err
return nil, err
}

return []string{taID}, nil
Expand Down
4 changes: 2 additions & 2 deletions scheme/parsec-cca/store_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,13 +43,13 @@ func (s StoreHandler) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string

err := evidence.FromCBOR(token.Data)
if err != nil {
return []string{""}, handler.BadEvidence(err)
return nil, handler.BadEvidence(err)
}
claims := evidence.Pat.PlatformClaims

taID, err := arm.GetTrustAnchorID(SchemeName, token.TenantId, claims)
if err != nil {
return []string{""}, err
return nil, err
}

return []string{taID}, nil
Expand Down
4 changes: 2 additions & 2 deletions scheme/parsec-tpm/store_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,12 @@ func (s StoreHandler) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string
var ev tpm.Evidence
err := ev.FromCBOR(token.Data)
if err != nil {
return []string{""}, handler.BadEvidence(err)
return nil, handler.BadEvidence(err)
}

kat := ev.Kat
if kat == nil {
return []string{""}, errors.New("no key attestation token to fetch Key ID")
return nil, errors.New("no key attestation token to fetch Key ID")
}
kid := *kat.KID
instance_id := base64.StdEncoding.EncodeToString(kid)
Expand Down
4 changes: 2 additions & 2 deletions scheme/psa-iot/store_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ func (s StoreHandler) SynthKeysFromTrustAnchor(tenantID string, ta *handler.Endo
func (s StoreHandler) GetTrustAnchorIDs(token *proto.AttestationToken) ([]string, error) {
psaToken, err := psatoken.DecodeAndValidateEvidenceFromCOSE(token.Data)
if err != nil {
return []string{""}, handler.BadEvidence(err)
return nil, handler.BadEvidence(err)
}

claims := psaToken.Claims

taID, err := arm.GetTrustAnchorID(SchemeName, token.TenantId, claims)
if err != nil {
return []string{""}, err
return nil, err
}

return []string{taID}, nil
Expand Down
2 changes: 1 addition & 1 deletion scheme/tpm-enacttrust/store_handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
31 changes: 29 additions & 2 deletions verification/api/challengeresponsesession.go
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// Copyright 2022-2023 Contributors to the Veraison project.
// Copyright 2022-2025 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0

// The api package implements the REST API defined in
// https://github.com/veraison/docs/blob/main/api/challenge-response
package api

import (
"encoding/base64"
"encoding/json"
"fmt"
"time"
Expand Down Expand Up @@ -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"`
Expand All @@ -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"`
Expand Down
6 changes: 3 additions & 3 deletions verification/api/handler.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022-2024 Contributors to the Veraison project.
// Copyright 2022-2025 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0
package api

Expand Down Expand Up @@ -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,
}
Expand Down Expand Up @@ -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)
Expand Down
40 changes: 35 additions & 5 deletions verification/api/handler_test.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// Copyright 2022-2024 Contributors to the Veraison project.
// Copyright 2022-2025 Contributors to the Veraison project.
// SPDX-License-Identifier: Apache-2.0
package api

Expand Down Expand Up @@ -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",
Expand All @@ -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",
Expand All @@ -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",
Expand Down Expand Up @@ -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()
Expand Down
5 changes: 2 additions & 3 deletions vts/trustedservices/trustedservices_grpc.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -507,12 +506,12 @@ func (c *GRPC) getTrustAnchors(id []string) ([]string, error) {
for _, taID := range id {
values, err := c.TaStore.Get(taID)
if err != nil {
return []string{""}, err
return nil, err
}

// For now, Veraison schemes only support one trust anchor per trustAnchorID
if len(values) != 1 {
return []string{""}, fmt.Errorf("found %d trust anchors, want 1", len(values))
return nil, fmt.Errorf("found %d trust anchors, want 1", len(values))
}
taValues = append(taValues, values[0])
}
Expand Down