diff --git a/README-CCA.md b/README-CCA.md index 50dcf74..efe3d5e 100644 --- a/README-CCA.md +++ b/README-CCA.md @@ -106,6 +106,60 @@ evcli cca check \ --claims=output-claims.json ``` +### Print + +Use the `cca print` subcommand to display the claims of a CCA attestation +token as pretty-printed JSON, without performing any signature checks. This will +perform the same well-formedness check as the `check` command, but will skip +cryptographic operations, meaning that a token can be inspected on its own without +providing any keys or other additional inputs. Structured JSON text will be written to +standard output. + +To print out the CCA attestation token in my.cbor: + +```shell +evcli cca print \ + --token=my.cbor +``` + +The claim set is printed to stdout in JSON format: + +```json +{ + "cca-platform-token": { + "cca-platform-profile": "http://arm.com/CCA-SSD/1.0.0", + "cca-platform-challenge": "Bea1iETGoM0ZOCBpuv2w5JRmKjrc+P3hFHjpM5Ua8XkP9d5ceOPbESPaCiB6i2ZVbgoi8Z7mS9wviZU7azJVXw==", + "cca-platform-implementation-id": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=", + "cca-platform-instance-id": "AQICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIC", + "cca-platform-config": "AQID", + "cca-platform-lifecycle": 12288, + "cca-platform-sw-components": [ + { + "measurement-value": "AwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwM=", + "signer-id": "BAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQ=" + } + ], + "cca-platform-service-indicator": "https://veraison.example/v1/challenge-response", + "cca-platform-hash-algo-id": "sha-256" + }, + "cca-realm-delegated-token": { + "cca-realm-challenge": "QUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQkFCQUJBQg==", + "cca-realm-personalization-value": "QURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBREFEQURBRA==", + "cca-realm-initial-measurement": "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", + "cca-realm-extensible-measurements": [ + "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", + "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", + "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==", + "Q0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQ0NDQw==" + ], + "cca-realm-hash-algo-id": "sha-256", + "cca-realm-public-key": "BIL70TKptcOWh5+7FTQNkFCXjlXHnVJ5oroOlYVPN+IM0vZPO3K1cLvXc+7iznaEJe31Re2+if+v4OlrvUbicPIHlsRIuY2vRqdk0nRC5ubthPjOyBfm7ManHTo959Z+zQ==", + "cca-realm-public-key-hash-algo-id": "sha-512" + } +} + +``` + ### Verify The `cca verify-as` subcommand allows you to interact with the Veraison diff --git a/README-PSA.md b/README-PSA.md index 5c1b02c..709e89a 100644 --- a/README-PSA.md +++ b/README-PSA.md @@ -106,6 +106,50 @@ evcli psa check \ --claims=output-claims.json ``` +### Print + +Use the `psa print` subcommand to display the claims of a PSA attestation +token as pretty-printed JSON, without performing any signature checks. This will +perform the same well-formedness check as the `check` command, but will skip +cryptographic operations, meaning that a token can be inspected on its own without +providing any keys or other additional inputs. Structured JSON text will be written to +standard output. + +To print out the PSA attestation token in my.cbor: + +```shell +evcli psa print --token=my.cbor +``` + +The claim set is printed to stdout in JSON format: + +```json +{ + "eat-profile": "http://arm.com/psa/2.0.0", + "psa-client-id": 1, + "psa-security-lifecycle": 12288, + "psa-implementation-id": "UFFSU1RVVldQUVJTVFVWV1BRUlNUVVZXUFFSU1RVVlc=", + "psa-boot-seed": "3q2+796tvu/erb7v3q2+796tvu/erb7v3q2+796tvu8=", + "psa-hardware-version": "1234567890123", + "psa-software-components": [ + { + "measurement-type": "BL", + "measurement-value": "AAECBAABAgQAAQIEAAECBAABAgQAAQIEAAECBAABAgQ=", + "signer-id": "UZIA/1GSAP9RkgD/UZIA/1GSAP9RkgD/UZIA/1GSAP8=" + }, + { + "measurement-type": "PRoT", + "measurement-value": "BQYHCAUGBwgFBgcIBQYHCAUGBwgFBgcIBQYHCAUGBwg=", + "signer-id": "UZIA/1GSAP9RkgD/UZIA/1GSAP9RkgD/UZIA/1GSAP8=" + } + ], + "psa-nonce": "AAECAwABAgMAAQIDAAECAwABAgMAAQIDAAECAwABAgM=", + "psa-instance-id": "AaChoqOgoaKjoKGio6ChoqOgoaKjoKGio6ChoqOgoaKj", + "psa-verification-service-indicator": "https://psa-verifier.org", + "psa-certification-reference": "1234567890123-12345", +} +``` + ### Verify The `psa verify-as` subcommand allows you to interact with the Veraison diff --git a/cmd/cca/check.go b/cmd/cca/check.go index 08a09e4..d6d043d 100644 --- a/cmd/cca/check.go +++ b/cmd/cca/check.go @@ -4,11 +4,11 @@ package cca import ( + "encoding/json" "fmt" "github.com/spf13/afero" "github.com/spf13/cobra" - "github.com/veraison/ccatoken" "github.com/veraison/evcli/v2/common" ) @@ -73,7 +73,7 @@ es256.jwk and dump the embedded claims to standard output: ) } - claims, err := t.MarshalJSON() + claims, err := json.MarshalIndent(t, "", " ") if err != nil { return fmt.Errorf("serializing CCA evidence: %w", err) } @@ -117,18 +117,3 @@ func init() { panic(err) } } - -func loadTokenFromFile(fs afero.Fs, fn string) (*ccatoken.Evidence, error) { - buf, err := afero.ReadFile(fs, fn) - if err != nil { - return nil, err - } - - e := ccatoken.Evidence{} - - if err = e.FromCBOR(buf); err != nil { - return nil, err - } - - return &e, nil -} diff --git a/cmd/cca/cmd.go b/cmd/cca/cmd.go index 14feb4c..5b996c4 100644 --- a/cmd/cca/cmd.go +++ b/cmd/cca/cmd.go @@ -27,4 +27,5 @@ func init() { Cmd.AddCommand(createCmd) Cmd.AddCommand(checkCmd) Cmd.AddCommand(verifyAsCmd) + Cmd.AddCommand(printCmd) } diff --git a/cmd/cca/common.go b/cmd/cca/common.go index 1f6dce8..b23c850 100644 --- a/cmd/cca/common.go +++ b/cmd/cca/common.go @@ -59,3 +59,18 @@ func loadUnValidatedCCAClaimsFromFile(fs afero.Fs, fn string) (psatoken.IClaims, } return p, r, nil } + +func loadTokenFromFile(fs afero.Fs, fn string) (*ccatoken.Evidence, error) { + buf, err := afero.ReadFile(fs, fn) + if err != nil { + return nil, err + } + + e := ccatoken.Evidence{} + + if err = e.FromCBOR(buf); err != nil { + return nil, err + } + + return &e, nil +} diff --git a/cmd/cca/print.go b/cmd/cca/print.go new file mode 100644 index 0000000..14bf6ac --- /dev/null +++ b/cmd/cca/print.go @@ -0,0 +1,66 @@ +// Copyright 2024 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package cca + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/afero" + "github.com/spf13/cobra" + "github.com/veraison/evcli/v2/common" +) + +var ( + printTokenFile *string +) + +var printCmd = NewPrintCmd(common.Fs) + +func NewPrintCmd(fs afero.Fs) *cobra.Command { + cmd := &cobra.Command{ + Use: "print", + Short: "Write the claims in the supplied CCA attestation token to standard output", + Long: `Write the claims in the supplied CCA attestation token to standard output. + +To pretty-print a CCA attestation token contained in my.cbor: + + evcli cca print --token=my.cbor + +Or, equivalently: + + evcli cca print -t my.cbor + `, + RunE: func(cmd *cobra.Command, args []string) error { + t, err := loadTokenFromFile(fs, *printTokenFile) + if err != nil { + return fmt.Errorf( + "loading CCA evidence from %s: %w", + *printTokenFile, err, + ) + } + + claims, err := json.MarshalIndent(t, "", " ") + if err != nil { + return fmt.Errorf("serializing CCA evidence: %w", err) + } + + fmt.Printf("%s\n", string(claims)) + + return nil + }, + } + + printTokenFile = cmd.Flags().StringP( + "token", "t", "", "CBOR file containing the CCA attestation token to be printed", + ) + + return cmd +} + +func init() { + if err := printCmd.MarkFlagRequired("token"); err != nil { + panic(err) + } +} diff --git a/cmd/cca/print_test.go b/cmd/cca/print_test.go new file mode 100644 index 0000000..8b8a17a --- /dev/null +++ b/cmd/cca/print_test.go @@ -0,0 +1,64 @@ +// Copyright 2024 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package cca + +import ( + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_PrintCmd_ok(t *testing.T) { + fs := afero.NewMemMapFs() + + err := afero.WriteFile(fs, "ccatoken.CBOR", testValidCCAToken, 0644) + require.NoError(t, err) + + cmd := NewPrintCmd(fs) + cmd.SetArgs( + []string{ + "--token=ccatoken.CBOR", + }, + ) + + err = cmd.Execute() + assert.NoError(t, err) +} + +func Test_PrintCmd_token_not_found(t *testing.T) { + fs := afero.NewMemMapFs() + + cmd := NewPrintCmd(fs) + cmd.SetArgs( + []string{ + "--token=ccatoken.cbor", + }, + ) + + expectedErr := `loading CCA evidence from ccatoken.cbor: open ccatoken.cbor: file does not exist` + + err := cmd.Execute() + assert.EqualError(t, err, expectedErr) +} + +func Test_PrintCmd_token_invalid_format(t *testing.T) { + fs := afero.NewMemMapFs() + + err := afero.WriteFile(fs, "ccatoken.cbor", testInvalidCCAToken, 0644) + require.NoError(t, err) + + cmd := NewPrintCmd(fs) + cmd.SetArgs( + []string{ + "--token=ccatoken.cbor", + }, + ) + + expectedErr := `loading CCA evidence from ccatoken.cbor: CBOR decoding of CCA evidence failed: unexpected EOF` + + err = cmd.Execute() + assert.EqualError(t, err, expectedErr) +} diff --git a/cmd/psa/cmd.go b/cmd/psa/cmd.go index 9d0a750..159369e 100644 --- a/cmd/psa/cmd.go +++ b/cmd/psa/cmd.go @@ -27,4 +27,5 @@ func init() { Cmd.AddCommand(createCmd) Cmd.AddCommand(checkCmd) Cmd.AddCommand(verifyAsCmd) + Cmd.AddCommand(printCmd) } diff --git a/cmd/psa/print.go b/cmd/psa/print.go new file mode 100644 index 0000000..534a750 --- /dev/null +++ b/cmd/psa/print.go @@ -0,0 +1,64 @@ +// Copyright 2024 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package psa + +import ( + "encoding/json" + "fmt" + + "github.com/spf13/afero" + "github.com/spf13/cobra" + "github.com/veraison/evcli/v2/common" +) + +var ( + printTokenFile *string +) + +var printCmd = NewPrintCmd(common.Fs) + +func NewPrintCmd(fs afero.Fs) *cobra.Command { + cmd := &cobra.Command{ + Use: "print", + Short: "print the contents of a PSA attestation token to the standard output", + Long: `Print the contents of the given PSA attestation token to the standard +output, without performing any cryptographic checks. + +Print a PSA attestation token contained in my.cbor: + + evcli psa print --token=my.cbor + +Or, equivalently: + + evcli psa print -t my.cbor + `, + RunE: func(cmd *cobra.Command, args []string) error { + t, err := loadTokenFromFile(fs, *printTokenFile) + if err != nil { + return err + } + + claims, err := json.MarshalIndent(t.Claims, "", " ") + if err != nil { + return fmt.Errorf("claims extraction failed: %w", err) + } + + fmt.Printf("%s\n", claims) + + return nil + }, + } + + printTokenFile = cmd.Flags().StringP( + "token", "t", "", "CBOR file containing the PSA attestation token to be verified", + ) + + return cmd +} + +func init() { + if err := printCmd.MarkFlagRequired("token"); err != nil { + panic(err) + } +} diff --git a/cmd/psa/print_test.go b/cmd/psa/print_test.go new file mode 100644 index 0000000..8e1e871 --- /dev/null +++ b/cmd/psa/print_test.go @@ -0,0 +1,64 @@ +// Copyright 2024 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +package psa + +import ( + "testing" + + "github.com/spf13/afero" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_PrintCmd_ok(t *testing.T) { + fs := afero.NewMemMapFs() + + err := afero.WriteFile(fs, "psatoken.cbor", testValidP2PSAToken, 0644) + require.NoError(t, err) + + cmd := NewPrintCmd(fs) + cmd.SetArgs( + []string{ + "--token=psatoken.cbor", + }, + ) + + err = cmd.Execute() + assert.NoError(t, err) +} + +func Test_PrintCmd_token_not_found(t *testing.T) { + fs := afero.NewMemMapFs() + + cmd := NewPrintCmd(fs) + cmd.SetArgs( + []string{ + "--token=psatoken.cbor", + }, + ) + + expectedErr := `open psatoken.cbor: file does not exist` + + err := cmd.Execute() + assert.EqualError(t, err, expectedErr) +} + +func Test_PrintCmd_bad_token(t *testing.T) { + fs := afero.NewMemMapFs() + + err := afero.WriteFile(fs, "psatoken.cbor", testInvalidPSAToken, 0644) + require.NoError(t, err) + + cmd := NewPrintCmd(fs) + cmd.SetArgs( + []string{ + "--token=psatoken.cbor", + }, + ) + + expectedErr := `failed CBOR decoding for CWT: cbor: invalid COSE_Sign1_Tagged object` + + err = cmd.Execute() + assert.EqualError(t, err, expectedErr) +}