ear

package module
v1.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 22, 2023 License: Apache-2.0 Imports: 16 Imported by: 13

README

Attestation Results for Secure Interactions

Go Reference ci

The ear package provides a golang API for working with EAR (EAT Attesation Result), an EAT/JWT serialisation of the Attestation Result for Secure Interactions (AR4SI) information model.

A command line interface utility (arc) is also provided to create, verify and display EAR attestation result payloads. To install it:

go install github.com/veraison/ear/arc@latest

Documentation

Overview

Package ear implements an EAT attestation result format based on the information model defined in https://datatracker.ietf.org/doc/draft-ietf-rats-ar4si/

Construction

An AttestationResult object is constructed by populating the relevant fields. The mandatory attributes are: status, timestamp and profile. For example, a simple AttestationResult payload with only the bare minimum claims could be created as follows:

myStatus := TrustTierAffirming
myTimestamp := time.Now().Format(time.RFC3339)
myPolicyID := `https://veraison.example/policy/1A4DF345-B512-4F3B-8461-967DE7F60ECA`
myProfile := EatProfile

ar := AttestationResult{
	Status:            &myStatus,
	Timestamp:         &testTimestamp,
	AppraisalPolicyID: &testPolicyID,
	Profile:           &testProfile,
}

A richer one would normally include the Trustworthiness Vector, which provides details about the appraised attester components. In the example below, the attester has been assessed as genuine, i.e., all claims are in the "affirming" range. (See §2.3 of draft-ietf-rats-ar4si-03 for details about the allowed values and their meaning.)

tv := TrustVector{
	InstanceIdentity: 2,
	Configuration: 2,
	Executables: 2,
	Hardware: 2,
}

ar.TrustVector := &tv

Signing and Serializing

Once the AttestationResult is populated, it can be signed (i.e., wrapped in a JWT) by invoking the Sign method:

myECDSAPrivateKey = `{
	"kty": "EC",
	"crv": "P-256",
	"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
	"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4",
	"d": "V8kgd2ZBRuh2dgyVINBUqpPDr7BOMGcF22CQMIUHtNM"
}`

sigK, _ := jwk.ParseKey([]byte(myECDSAPrivateKey))

buf, _ = ar.Sign(jwa.ES256, sigK)

In this case, the returned buf contains a signed ES256 JWT with the JSON serialization of the AttestationResult object as its payload. This is the usual JWT format that can be used as-is for interchange with other applications.

Parsing and Verifying

On the consumer end of the protocol, when the EAT containing the attestation result is received from a veraison verifier, the relying party needs to first parse it and verify the signature using the Verify method:

myECDSAPublicKey = `{
	"kty": "EC",
	"crv": "P-256",
	"x": "usWxHK2PmfnHKwXPS54m0kTcGJ90UiglWiGahtagnv8",
	"y": "IBOL-C3BttVivg-lSreASjpkttcsz-1rb7btKLv8EX4"
}`

vfyK, _ := jwk.ParseKey([]byte(myECDSAPublicKey))

var ar AttestationResult

err := ar.Verify(token, jwa.ES256, vfyK)
if err != nil {
	// handle verification error
}

If there are no errors, the relying party can trust the attestation result and inspect the relevant fields to decide about the trustworthiness of the attested entity.

if *ar.Status != TrustTierAffirming {
	// handle troubles with appraisal
}

Pretty printing

The package provides a Report method that allows pretty printing of the Trustworthiness Vector. The caller can request a short summary or a detailed printout, as well as using colors when displaying the claims' values.

short, color := true, true

fmt.Print(ar.TrustVector.Report(short, color))
Example (Colors)
j := `{
		"submods": {
			"test": {
				"ear.status": "contraindicated",
				"ear.appraisal-policy-id": "policy://test/01234",
				"ear.trustworthiness-vector": {
					"instance-identity": 96,
					"configuration": 96,
					"executables": 32,
					"hardware": 2
				}
			}
		},
		"iat":1666091373,
		"eat_profile": "tag:github.com,2023:veraison/ear"
	}`

var ar AttestationResult
_ = ar.UnmarshalJSON([]byte(j))

short, color := true, true

fmt.Print(ar.Submods["test"].TrustVector.Report(short, color))
Output:

Instance Identity [\033[41mcontraindicated\033[0m]: recognized but not trustworthy
Configuration [\033[41mcontraindicated\033[0m]: unacceptable security vulnerabilities
Executables [\033[43mwarning\033[0m]: recognized but known bugs or vulnerabilities
File System [\033[47mnone\033[0m]: no claim being made
Hardware [\033[42maffirming\033[0m]: genuine
Runtime Opaque [\033[47mnone\033[0m]: no claim being made
Storage Opaque [\033[47mnone\033[0m]: no claim being made
Sourced Data [\033[47mnone\033[0m]: no claim being made
Example (Decode_veraison_extensions)
j := `{
		"eat_profile": "tag:github.com,2023:veraison/ear",
		"iat": 1666091373,
		"submods": {
			"test": {
				"ear.status": "affirming",
				"ear.appraisal-policy-id": "policy://test/01234",
				"ear.veraison.annotated-evidence": {
					"k1": "v1",
					"k2": "v2"
				},
				"ear.veraison.key-attestation": {
					"akpub": "YWtwdWIK"
				},
				"ear.veraison.policy-claims": {
					"bar": "baz",
					"foo": "bar"
				}
			}
		},
		"ear.verifier-id": {
			"developer": "Contributors to the Veraison project",
			"build": "v1.1.23"
		},
		"ear.veraison.tee-info": {
			"tee-name": "aws-nitro",
			"evidence-id": "405e0c3127e455ebc22361210b43ca9499ca80d3f6b1dc79b89fa35290cee3d9",
			"evidence": "ZXZpZGVuY2U="
		}
	}`
var ar AttestationResult
_ = ar.UnmarshalJSON([]byte(j))

fmt.Println(TrustTierToString[*ar.Submods["test"].Status])
fmt.Println((*ar.Submods["test"].VeraisonAnnotatedEvidence)["k1"])
fmt.Println((*ar.Submods["test"].VeraisonPolicyClaims)["bar"])
fmt.Println((*ar.Submods["test"].VeraisonKeyAttestation)["akpub"])
fmt.Println(*ar.VeraisonTeeInfo.TeeName)
fmt.Println(*ar.VeraisonTeeInfo.EvidenceID)
fmt.Println(*ar.VeraisonTeeInfo.Evidence)
Output:

affirming
v1
baz
YWtwdWIK
aws-nitro
405e0c3127e455ebc22361210b43ca9499ca80d3f6b1dc79b89fa35290cee3d9
[101 118 105 100 101 110 99 101]
Example (Encode_hefty)
rawEvidence := B64Url{0xde, 0xad, 0xbe, 0xef}

ar := AttestationResult{
	Submods: map[string]*Appraisal{
		"test": {
			Status: &testStatus,
			TrustVector: &TrustVector{
				InstanceIdentity: 2,
				Configuration:    2,
				Executables:      3,
				FileSystem:       2,
				Hardware:         2,
				RuntimeOpaque:    2,
				StorageOpaque:    2,
				SourcedData:      2,
			},
			AppraisalPolicyID: &testPolicyID,
		},
	},
	RawEvidence: &rawEvidence,
	IssuedAt:    &testIAT,
	VerifierID:  &testVerifierID,
	Profile:     &testProfile,
	AttestationResultExtensions: AttestationResultExtensions{
		VeraisonTeeInfo: &VeraisonTeeInfo{
			TeeName:    &testTeeName,
			EvidenceID: &testEvidenceID,
			Evidence:   &testEvidence,
		},
	},
}

j, _ := ar.MarshalJSON()

fmt.Println(string(j))
Output:

{"ear.raw-evidence":"3q2-7w","ear.veraison.tee-info":{"tee-name":"aws-nitro","evidence-id":"405e0c3127e455ebc22361210b43ca9499ca80d3f6b1dc79b89fa35290cee3d9","evidence":"ZXZpZGVuY2U="},"ear.verifier-id":{"build":"rrtrap-v1.0.0","developer":"Acme Inc."},"eat_profile":"tag:github.com,2023:veraison/ear","iat":1666091373,"submods":{"test":{"ear.appraisal-policy-id":"policy://test/01234","ear.status":"affirming","ear.trustworthiness-vector":{"configuration":2,"executables":3,"file-system":2,"hardware":2,"instance-identity":2,"runtime-opaque":2,"sourced-data":2,"storage-opaque":2}}}}
Example (Encode_minimalist)
ar := AttestationResult{
	Submods: map[string]*Appraisal{
		"test": {
			Status:            &testStatus,
			AppraisalPolicyID: &testPolicyID,
		},
	},

	IssuedAt:   &testIAT,
	VerifierID: &testVerifierID,
	Profile:    &testProfile,
}

j, _ := ar.MarshalJSON()

fmt.Println(string(j))
Output:

{"ear.verifier-id":{"build":"rrtrap-v1.0.0","developer":"Acme Inc."},"eat_profile":"tag:github.com,2023:veraison/ear","iat":1666091373,"submods":{"test":{"ear.appraisal-policy-id":"policy://test/01234","ear.status":"affirming"}}}
Example (Encode_veraison_extensions)
ar := testAttestationResultsWithVeraisonExtns

j, _ := ar.MarshalJSON()

fmt.Println(string(j))
Output:

{"ear.verifier-id":{"build":"rrtrap-v1.0.0","developer":"Acme Inc."},"eat_profile":"tag:github.com,2023:veraison/ear","iat":1666091373,"submods":{"test":{"ear.appraisal-policy-id":"policy://test/01234","ear.status":"affirming","ear.veraison.annotated-evidence":{"k1":"v1","k2":"v2"},"ear.veraison.key-attestation":{"akpub":"YWtwdWIK"},"ear.veraison.policy-claims":{"bar":"baz","foo":"bar"}}}}

Index

Examples

Constants

View Source
const (

	// general
	VerifierMalfunctionClaim    = TrustClaim(-1)
	NoClaim                     = TrustClaim(0)
	UnexpectedEvidenceClaim     = TrustClaim(1)
	CryptoValidationFailedClaim = TrustClaim(99)

	// instance identity
	TrustworthyInstanceClaim   = TrustClaim(2)
	UntrustworthyInstanceClaim = TrustClaim(96)
	UnrecognizedInstanceClaim  = TrustClaim(97)

	// config
	ApprovedConfigClaim      = TrustClaim(2)
	NoConfigVulnsClaim       = TrustClaim(3)
	UnsafeConfigClaim        = TrustClaim(32)
	UnsupportableConfigClaim = TrustClaim(96)

	// executables & runtime
	ApprovedRuntimeClaim        = TrustClaim(2)
	ApprovedBootClaim           = TrustClaim(3)
	UnsafeRuntimeClaim          = TrustClaim(32)
	UnrecognizedRuntimeClaim    = TrustClaim(33)
	ContraindicatedRuntimeClaim = TrustClaim(96)

	// file system
	ApprovedFilesClaim        = TrustClaim(2)
	UnrecognizedFilesClaim    = TrustClaim(32)
	ContraindicatedFilesClaim = TrustClaim(96)

	// hardware
	GenuineHardwareClaim         = TrustClaim(2)
	UnsafeHardwareClaim          = TrustClaim(32)
	ContraindicatedHardwareClaim = TrustClaim(96)
	UnrecognizedHardwareClaim    = TrustClaim(97)

	// opaque runtime
	EncryptedMemoryRuntimeClaim = TrustClaim(2)
	IsolatedMemoryRuntimeClaim  = TrustClaim(32)
	VisibleMemoryRuntimeClaim   = TrustClaim(96)

	// opaque storage
	HwKeysEncryptedSecretsClaim = TrustClaim(2)
	SwKeysEncryptedSecretsClaim = TrustClaim(32)
	UnencryptedSecretsClaim     = TrustClaim(96)

	// sourced data
	TrustedSourcesClaim         = TrustClaim(2)
	UntrustedSourcesClaim       = TrustClaim(32)
	ContraindicatedSourcesClaim = TrustClaim(96)
)
View Source
const EatProfile = "tag:github.com,2023:veraison/ear"

EatProfile is the EAT profile implemented by this package

Variables

View Source
var (
	TrustTierToString = map[TrustTier]string{
		TrustTierNone:            "none",
		TrustTierAffirming:       "affirming",
		TrustTierWarning:         "warning",
		TrustTierContraindicated: "contraindicated",
	}

	StringToTrustTier = map[string]TrustTier{
		"none":            TrustTierNone,
		"affirming":       TrustTierAffirming,
		"warning":         TrustTierWarning,
		"contraindicated": TrustTierContraindicated,
	}

	IntToTrustTier = map[int]TrustTier{
		0:  TrustTierNone,
		2:  TrustTierAffirming,
		32: TrustTierWarning,
		96: TrustTierContraindicated,
	}
)

Functions

This section is empty.

Types

type Appraisal added in v1.0.0

type Appraisal struct {
	Status            *TrustTier   `json:"ear.status"`
	TrustVector       *TrustVector `json:"ear.trustworthiness-vector,omitempty"`
	AppraisalPolicyID *string      `json:"ear.appraisal-policy-id,omitempty"`

	AppraisalExtensions
}

Appraisal represents the result of an evidence appraisal by the verifier. It wraps the AR4SI trustworthiness vector together with other metadata that are relevant to establish the appraisal context - the evidence itself, the appraisal policy used, the time of appraisal.

func ToAppraisal added in v1.0.0

func ToAppraisal(v interface{}) (*Appraisal, error)

func (Appraisal) AsMap added in v1.0.0

func (o Appraisal) AsMap() map[string]interface{}

AsMap returns a map[string]interface{} with EAR Appraisal claim names mapped onto corresponding values.

func (*Appraisal) UpdateStatusFromTrustVector added in v1.0.0

func (o *Appraisal) UpdateStatusFromTrustVector()

UpdateStatusFromTrustVector ensure that Status trustworthiness is not higher than is warranted by trust vector claims. For every claim that has been made (i.e. is not in TrustTierNone), if the claim's trust tier is lower than that of the Status, adjust the status to the claim's tier. This means that the overall result will not assert to be more trustworthy than individual vector claims (though it could be less trustworthy if had been manually set that way).

type AppraisalExtensions added in v1.0.0

type AppraisalExtensions struct {
	VeraisonAnnotatedEvidence *map[string]interface{} `json:"ear.veraison.annotated-evidence,omitempty"`
	VeraisonPolicyClaims      *map[string]interface{} `json:"ear.veraison.policy-claims,omitempty"`
	VeraisonKeyAttestation    *map[string]interface{} `json:"ear.veraison.key-attestation,omitempty"`
}

AppraisalExtensions contains any proprietary claims that can be optionally attached to the Appraisal. For now only veraison-specific extensions are supported.

func (AppraisalExtensions) GetKeyAttestation added in v1.1.0

func (o AppraisalExtensions) GetKeyAttestation() (any, error)

GetKeyAttestation returns the decoded public key carried in the "ear.veraison.key-attestation" claim. The returned key type is one supported by x509.ParsePKIXPublicKey.

func (*AppraisalExtensions) SetKeyAttestation added in v1.1.0

func (o *AppraisalExtensions) SetKeyAttestation(pub any) error

SetKeyAttestation sets the value of `akpub` in the "ear.veraison.key-attestation" claim. The following key types are currently supported: *rsa.PublicKey, *ecdsa.PublicKey, ed25519.PublicKey (not a pointer). Unsupported key types result in an error.

type AttestationResult

type AttestationResult struct {
	Profile     *string               `json:"eat_profile"`
	VerifierID  *VerifierIdentity     `json:"ear.verifier-id"`
	RawEvidence *B64Url               `json:"ear.raw-evidence,omitempty"`
	IssuedAt    *int64                `json:"iat"`
	Nonce       *string               `json:"eat_nonce,omitempty"`
	Submods     map[string]*Appraisal `json:"submods"`

	AttestationResultExtensions
}

AttestationResult represents the result of one or more evidence Appraisals by the verifier. It is serialized to JSON and signed by the verifier using JWT.

func NewAttestationResult

func NewAttestationResult(
	submodName string,
	verifierBuild string,
	verifierDeveloper string,
) *AttestationResult

NewAttestationResult returns a pointer to a new fully-initialized AttestationResult.

func (AttestationResult) AsMap

func (o AttestationResult) AsMap() map[string]interface{}

AsMap returns a map[string]interface{} with EAR claim names mapped onto corresponding values.

func (AttestationResult) MarshalJSON

func (o AttestationResult) MarshalJSON() ([]byte, error)

MarshalJSON validates and serializes to JSON an AttestationResult object

func (AttestationResult) MarshalJSONIndent

func (o AttestationResult) MarshalJSONIndent(prefix, indent string) ([]byte, error)

MarshalJSONIndent is like MarshalJSON but applies Indent to format the output. Each JSON element in the output will begin on a new line beginning with prefix followed by one or more copies of indent according to the indentation nesting.

func (AttestationResult) Sign

func (o AttestationResult) Sign(alg jwa.KeyAlgorithm, key interface{}) ([]byte, error)

Sign validates the AttestationResult object, encodes it to JSON and wraps it in a JWT using the supplied private key for signing. The key must be compatible with the requested signing algorithm. On success, the complete JWT token is returned.

func (*AttestationResult) UnmarshalJSON

func (o *AttestationResult) UnmarshalJSON(data []byte) error

UnmarshalJSON de-serializes an AttestationResult object from its JSON representation and validates it.

func (*AttestationResult) UpdateStatusFromTrustVector

func (o *AttestationResult) UpdateStatusFromTrustVector()

UpdateStatusFromTrustVector ensure that Status trustworthiness of each Appraisal is not higher than is warranted by its trust vector claims. For every claim that has been made (i.e. is not in TrustTierNone), if the claim's trust tier is lower than that of the Status, adjust the status to the claim's tier. This means that the overall result will not assert to be more trustworthy than individual vector claims (though it could be less trustworthy if had been manually set that way).

func (*AttestationResult) Verify

func (o *AttestationResult) Verify(data []byte, alg jwa.KeyAlgorithm, key interface{}) error

Verify cryptographically verifies the JWT data using the supplied key and algorithm. The payload is then parsed and validated. On success, the target AttestationResult object is populated with the decoded claims (possibly including the Trustworthiness vector).

type AttestationResultExtensions added in v1.1.0

type AttestationResultExtensions struct {
	VeraisonTeeInfo *VeraisonTeeInfo `json:"ear.veraison.tee-info,omitempty"`
}

type B64Url added in v1.0.0

type B64Url []byte

B64Url is base64url (§5 of RFC4648) without padding. bstr MUST be base64url encoded as per EAT §7.2.2 "JSON Interoperability".

func (B64Url) MarshalJSON added in v1.0.0

func (o B64Url) MarshalJSON() ([]byte, error)

type TrustClaim

type TrustClaim int8

trustworthiness claim

func ToTrustClaim

func ToTrustClaim(v interface{}) (*TrustClaim, error)

func (TrustClaim) GetTier

func (o TrustClaim) GetTier() TrustTier

TrustTier provides the trust tier bucket of the trustworthiness claim

func (TrustClaim) IsAffirming

func (o TrustClaim) IsAffirming() bool

func (TrustClaim) IsContraindicated

func (o TrustClaim) IsContraindicated() bool

func (TrustClaim) IsNone

func (o TrustClaim) IsNone() bool

func (TrustClaim) IsWarning

func (o TrustClaim) IsWarning() bool

type TrustTier

type TrustTier int8

TrustTier represents the overall state of an evidence appraisal.

TrustTierNone means appraisal could not be conducted for whatever reason (e.g., a processing error).

TrustTierAffirming means appraisal was fully successful and the attester can be considered trustworthy.

TrustTierWarning means appraisal was mostly successful, but there are specific checks that need further attention from the relying party to assess whether the attester can be considered trustworthy or not.

TrustTierContraindicated means some specific checks have failed and the attester cannot be considered trustworthy.

const (
	TrustTierNone            TrustTier = 0
	TrustTierAffirming       TrustTier = 2
	TrustTierWarning         TrustTier = 32
	TrustTierContraindicated TrustTier = 96
)

func NewTrustTier

func NewTrustTier(v interface{}) *TrustTier

NewTrustTier returns a pointer to a newly-created TrustTier that has the specified value. If the provided value is invalid for a TrustTier, TrustTierNone will be used instead. (This is essentially a Must- wrapper for ToTrustTier().)

func ToTrustTier

func ToTrustTier(v interface{}) (*TrustTier, error)

func (TrustTier) ColorString

func (o TrustTier) ColorString() string

func (TrustTier) Format

func (o TrustTier) Format(color bool) string

func (TrustTier) MarshalJSON

func (o TrustTier) MarshalJSON() ([]byte, error)

func (TrustTier) String

func (o TrustTier) String() string

func (*TrustTier) UnmarshalJSON

func (o *TrustTier) UnmarshalJSON(data []byte) error

type TrustVector

type TrustVector struct {
	InstanceIdentity TrustClaim `json:"instance-identity,omitempty"`
	Configuration    TrustClaim `json:"configuration,omitempty"`
	Executables      TrustClaim `json:"executables,omitempty"`
	FileSystem       TrustClaim `json:"file-system,omitempty"`
	Hardware         TrustClaim `json:"hardware,omitempty"`
	RuntimeOpaque    TrustClaim `json:"runtime-opaque,omitempty"`
	StorageOpaque    TrustClaim `json:"storage-opaque,omitempty"`
	SourcedData      TrustClaim `json:"sourced-data,omitempty"`
}

TrustVector is an implementation of the Trustworthiness Vector (and Claims) described in §2.3 of draft-ietf-rats-ar4si-03, using a JSON serialization.

func ToTrustVector

func ToTrustVector(v interface{}) (*TrustVector, error)

func (TrustVector) AsMap

func (o TrustVector) AsMap() map[string]TrustClaim

AsMap() returns a map[string]TrustClaim with claims names mapped onto corresponding TrustClaim values.

func (TrustVector) Report

func (o TrustVector) Report(short, color bool) string

Report provides an annotated view of the TrustVector state. short and color are used to control the level of details and the use of colors when printing the trust tier, respectively

func (*TrustVector) SetAll

func (o *TrustVector) SetAll(c TrustClaim)

SetAll sets all vector elements to the specified claim. This is primarily useful with globally-applicable claims such as -1 (verifier malfunction), 0 (no claim, in order to "reset" the vector), or 99 (cryptographic validation failed).

type VeraisonTeeInfo added in v1.1.0

type VeraisonTeeInfo struct {
	TeeName    *string `json:"tee-name"`
	EvidenceID *string `json:"evidence-id"`
	Evidence   *[]byte `json:"evidence,omitempty"`
}

func ToVeraisonTeeInfo added in v1.1.0

func ToVeraisonTeeInfo(v interface{}) (*VeraisonTeeInfo, error)

func (VeraisonTeeInfo) Validate added in v1.1.0

func (o VeraisonTeeInfo) Validate() error

type VerifierIdentity added in v1.0.0

type VerifierIdentity struct {
	// Build uniquely identifies the software build running the verifier.
	Build *string `json:"build"`
	// Developer uniquely identifies the organizational unit responsible
	// for this build.
	Developer *string `json:"developer"`
}

VerifierIdentity is the verifier software identification as defined by AR4SI:

https://datatracker.ietf.org/doc/html/draft-ietf-rats-ar4si-03#section-2.2.2

func ToVerifierIdentity added in v1.0.0

func ToVerifierIdentity(v interface{}) (*VerifierIdentity, error)

Directories

Path Synopsis
arc
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL