# Open Policy Engine Backend ## Usage To use this backend, specify `"opa"` as the `policy.backend` in the config: ```golang import "github.com/veraison/services/config" cfg := config.Config{ "policy.backend": "opa", } agent, err := CreateAgent(cfg) ``` This is also the default backend, so if no other backend is specified, `opa` will be used. ## Writing Policies Please see the OPA [official documentation](https://www.openpolicyagent.org/docs/latest/) for information on how to write Rego policies. This section describes what is necessary to implement a valid Veraison policy, and assumes a general familiarity with Rego. ### Package declaration Your policy should specify `policy` as the package. ```rego package policy ``` ### Evaluation Data Data to be evaluated is defined as the following variables in the `policy` package: `evidence` contains the JSON representation of `proto.EvidenceContext.Evidence` structure. This is the scheme-specific values extracted from the attestation token. `endorsements` is an array of endorsement JSON objects. Their structure is scheme-specific. `result` is a JSON object representing `proto.AttestationResult` that was generated by the scheme. `scheme` is the name of the attestation scheme. ### Rules You can update the attestation result by defining one or more of the following rules: - `status` - `hw_authenticity` - `sw_integrity` - `sw_up_to_dateness` - `config_integrity` - `runtime_integrity` - `certification_status` The first overwrites the overall attestation result status. The subsequent rules overwrite the corresponding entries in the trust vector. The rules must be defined such that they produce the string `"SUCCESS"` if the conditions have been met, and the string `"FAILURE"` otherwise. As various versions are often represented using Semantic Versioning, `policy` package provides a utility function, `semver_cmp` to aid in their comparison (note: currently, pre-release suffixes are not supported). For example: ```rego sw_integrity = "SUCCESS" { # note: This is an example. The evidence entries do not correspond to any # actual scheme. evidence["application-hash"] == "h0KPxSKAPTEGXnvOPPA/5HUJZjHl4Hu9eg/eYMTPJcc=" evidence["firmware-hash"] == "h0KPxSKAPTEGXnvOPPA/5HUJZjHl4Hu9eg/eYMTPJcc=" semver_cmp(evidence["firmware-version"], "1.2.0") >= 0 } else = "FAILURE" ``` #### Dealing With Multiple Attestation Schemes If you expect your policy to be applied to inputs from multiple attestation schemes, you differentiate between them using the `format` variable defined by the `policy` package. For example you can define rules for each scheme, and then alias the generic rules above to one of them based on the value of the `format` variable. For example: ```rego sw_up_to_dateness = psa_sw_up_to_dateness { format == "PSA_IOT" } else = enacttrust_sw_up_to_dateness { format == "TPM_ENACTTRUST" } psa_sw_up_to_dateness = "SUCCESS" { # ... } else = "FAILURE" enacttrust_sw_up_to_dateness = "SUCCESS" { # ... } else = "FAILURE" ``` ### Example Policy This is a example of a policy that sets the `SoftwareUpToDateness` value in the trust vector for `PSA_IOT` and `TPM_ENACTTRUST` schemes (neither scheme has built-in definition for software up-to-dateness, and so that entry is undefined in the results generated by the schemes). ```rego package policy # Use the psa_sw_up_to_dateness rules iff the attestation format is PSA_IOT, and # to enacttrust_sw_up_to_dateness iff the format is TPM_ENACTTRUST, otherwise, # sw_up_to_dateness will remain undefined. sw_up_to_dateness = psa_sw_up_to_dateness { format == "PSA_IOT" } else = enacttrust_sw_up_to_dateness { format == "TPM_ENACTTRUST" } # This sets sw-up-to-dateness trust vector value to success iff BL version is # 3.5 or greater, and to failure otherwise. psa_sw_up_to_dateness = "SUCCESS" { # there exisists some i such that... some i # ...the i'th software component has type "BL", and... evidence["psa-software-components"][i]["measurement-type"] == "BL" # ...the version of this component is greater or equal to 3.5. # (semver_cmp is defined by the policy package. It returns 1 if the first # parameter is greater than the second, -1 if it is less than the second, # and 0 if they are equal.) semver_cmp(evidence["psa-software-components"][i].version, "3.5") >= 0 } else = "FAILURE" # unless the above condition is met, return "FAILURE" # Unlike the PSA token, the EnactTrust token does not include information about # multiple sofware componets and instead has a single "firmware" entry. enacttrust_sw_up_to_dateness = "SUCCESS" { evidence["firmware"] >= 8 } else = "FAILURE" ```