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 ¶
- Constants
- Variables
- type Appraisal
- type AppraisalExtensions
- type AttestationResult
- func (o AttestationResult) AsMap() map[string]interface{}
- func (o AttestationResult) MarshalJSON() ([]byte, error)
- func (o AttestationResult) MarshalJSONIndent(prefix, indent string) ([]byte, error)
- func (o AttestationResult) Sign(alg jwa.KeyAlgorithm, key interface{}) ([]byte, error)
- func (o *AttestationResult) UnmarshalJSON(data []byte) error
- func (o *AttestationResult) UpdateStatusFromTrustVector()
- func (o *AttestationResult) Verify(data []byte, alg jwa.KeyAlgorithm, key interface{}) error
- type AttestationResultExtensions
- type B64Url
- type TrustClaim
- type TrustTier
- type TrustVector
- type VeraisonTeeInfo
- type VerifierIdentity
Examples ¶
Constants ¶
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) )
const EatProfile = "tag:github.com,2023:veraison/ear"
EatProfile is the EAT profile implemented by this package
Variables ¶
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 (Appraisal) AsMap ¶ added in v1.0.0
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
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.
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 (TrustTier) ColorString ¶
func (TrustTier) MarshalJSON ¶
func (*TrustTier) UnmarshalJSON ¶
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)