2023-01-18 10:49:55 -05:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
// Package idkeydigest provides type definitions for the `idkeydigest` value of SEV-SNP attestation.
|
2023-01-18 10:49:55 -05:00
|
|
|
package idkeydigest
|
|
|
|
|
|
|
|
import (
|
2023-05-03 05:11:53 -04:00
|
|
|
"bytes"
|
2023-01-18 10:49:55 -05:00
|
|
|
"encoding/hex"
|
|
|
|
"encoding/json"
|
2023-02-07 06:56:25 -05:00
|
|
|
"errors"
|
2023-01-18 10:49:55 -05:00
|
|
|
"fmt"
|
2023-03-21 07:46:49 -04:00
|
|
|
"strings"
|
2023-01-18 10:49:55 -05:00
|
|
|
)
|
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
//go:generate stringer -type=Enforcement
|
2023-03-21 07:46:49 -04:00
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
// Enforcement defines the behavior of the validator when the ID key digest is not found in the expected list.
|
|
|
|
type Enforcement uint32
|
2023-03-21 07:46:49 -04:00
|
|
|
|
|
|
|
const (
|
|
|
|
// Unknown is reserved for invalid configurations.
|
2023-04-06 11:00:56 -04:00
|
|
|
Unknown Enforcement = iota
|
|
|
|
// Equal will error if the reported signing key digest does not match any of the values in 'acceptedKeyDigests'.
|
|
|
|
Equal
|
|
|
|
// MAAFallback uses 'equal' checking for validation, but fallback to using Microsoft Azure Attestation (MAA)
|
|
|
|
// for validation if the reported digest does not match any of the values in 'acceptedKeyDigests'.
|
2023-03-21 07:46:49 -04:00
|
|
|
MAAFallback
|
2023-04-06 11:00:56 -04:00
|
|
|
// WarnOnly is the same as 'equal', but only prints a warning instead of returning an error if no match is found.
|
2023-03-21 07:46:49 -04:00
|
|
|
WarnOnly
|
|
|
|
)
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (e *Enforcement) UnmarshalJSON(b []byte) error {
|
2023-03-21 07:46:49 -04:00
|
|
|
return e.unmarshal(func(val any) error {
|
|
|
|
return json.Unmarshal(b, val)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (e Enforcement) MarshalJSON() ([]byte, error) {
|
2023-03-21 07:46:49 -04:00
|
|
|
return json.Marshal(e.String())
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (e *Enforcement) UnmarshalYAML(unmarshal func(any) error) error {
|
2023-03-21 07:46:49 -04:00
|
|
|
return e.unmarshal(unmarshal)
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalYAML implements the yaml.Marshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (e Enforcement) MarshalYAML() (any, error) {
|
2023-03-21 07:46:49 -04:00
|
|
|
return e.String(), nil
|
|
|
|
}
|
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
func (e *Enforcement) unmarshal(unmarshalFunc func(any) error) error {
|
2023-03-21 07:46:49 -04:00
|
|
|
// Check for legacy format: EnforceIDKeyDigest might be a boolean.
|
|
|
|
// If set to true, the value will be set to StrictChecking.
|
|
|
|
// If set to false, the value will be set to WarnOnly.
|
|
|
|
var legacyEnforce bool
|
|
|
|
legacyErr := unmarshalFunc(&legacyEnforce)
|
|
|
|
if legacyErr == nil {
|
|
|
|
if legacyEnforce {
|
2023-04-06 11:00:56 -04:00
|
|
|
*e = Equal
|
2023-03-21 07:46:49 -04:00
|
|
|
} else {
|
|
|
|
*e = WarnOnly
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var enforce string
|
|
|
|
if err := unmarshalFunc(&enforce); err != nil {
|
|
|
|
return errors.Join(
|
|
|
|
err,
|
|
|
|
fmt.Errorf("trying legacy format: %w", legacyErr),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
*e = EnforcePolicyFromString(enforce)
|
|
|
|
if *e == Unknown {
|
2023-04-06 11:00:56 -04:00
|
|
|
return fmt.Errorf("unknown Enforcement value: %q", enforce)
|
2023-03-21 07:46:49 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
// EnforcePolicyFromString returns Enforcement from string.
|
|
|
|
func EnforcePolicyFromString(s string) Enforcement {
|
2023-03-21 07:46:49 -04:00
|
|
|
s = strings.ToLower(s)
|
|
|
|
switch s {
|
2023-04-06 11:00:56 -04:00
|
|
|
case "equal":
|
|
|
|
return Equal
|
2023-03-21 07:46:49 -04:00
|
|
|
case "maafallback":
|
|
|
|
return MAAFallback
|
|
|
|
case "warnonly":
|
|
|
|
return WarnOnly
|
|
|
|
default:
|
|
|
|
return Unknown
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
// List is a list of trusted digest values for the ID key.
|
|
|
|
type List [][]byte
|
2023-01-18 10:49:55 -05:00
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
type encodedList []string
|
2023-01-18 10:49:55 -05:00
|
|
|
|
|
|
|
// encodedDigestLength is the length of a digest in hex encoding.
|
|
|
|
const encodedDigestLength = 2 * 48
|
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
// NewList creates a new IDKeyDigests from a list of digests.
|
|
|
|
func NewList(digests [][]byte) List {
|
|
|
|
idKeyDigests := make(List, len(digests))
|
2023-01-18 10:49:55 -05:00
|
|
|
copy(idKeyDigests, digests)
|
|
|
|
return idKeyDigests
|
|
|
|
}
|
|
|
|
|
2023-04-06 11:00:56 -04:00
|
|
|
// DefaultList returns the default list of accepted ID key digests.
|
|
|
|
func DefaultList() List {
|
|
|
|
return List{
|
|
|
|
{0x03, 0x56, 0x21, 0x58, 0x82, 0xa8, 0x25, 0x27, 0x9a, 0x85, 0xb3, 0x00, 0xb0, 0xb7, 0x42, 0x93, 0x1d, 0x11, 0x3b, 0xf7, 0xe3, 0x2d, 0xde, 0x2e, 0x50, 0xff, 0xde, 0x7e, 0xc7, 0x43, 0xca, 0x49, 0x1e, 0xcd, 0xd7, 0xf3, 0x36, 0xdc, 0x28, 0xa6, 0xe0, 0xb2, 0xbb, 0x57, 0xaf, 0x7a, 0x44, 0xa3},
|
2023-01-18 10:49:55 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-03 05:11:53 -04:00
|
|
|
// EqualTo returns true if the List of digests is equal to the other List.
|
|
|
|
func (d List) EqualTo(other List) bool {
|
|
|
|
if len(d) != len(other) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
for i := range d {
|
|
|
|
if !bytes.Equal(d[i], other[i]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
2023-01-18 10:49:55 -05:00
|
|
|
// MarshalYAML implements the yaml.Marshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (d List) MarshalYAML() (any, error) {
|
2023-01-18 10:49:55 -05:00
|
|
|
encodedIDKeyDigests := []string{}
|
|
|
|
for _, digest := range d {
|
|
|
|
encodedIDKeyDigests = append(encodedIDKeyDigests, hex.EncodeToString(digest))
|
|
|
|
}
|
|
|
|
return encodedIDKeyDigests, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalYAML implements the yaml.Unmarshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (d *List) UnmarshalYAML(unmarshal func(any) error) error {
|
|
|
|
var encodedDigests encodedList
|
2023-01-18 10:49:55 -05:00
|
|
|
if err := unmarshal(&encodedDigests); err != nil {
|
|
|
|
// Unmarshalling failed, IDKeyDigests might be a simple string instead of IDKeyDigests struct.
|
|
|
|
var unmarshalledString string
|
|
|
|
if legacyErr := unmarshal(&unmarshalledString); legacyErr != nil {
|
2023-02-07 06:56:25 -05:00
|
|
|
return errors.Join(
|
2023-01-18 10:49:55 -05:00
|
|
|
err,
|
|
|
|
fmt.Errorf("trying legacy format: %w", legacyErr),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
encodedDigests = append(encodedDigests, unmarshalledString)
|
|
|
|
}
|
|
|
|
if err := d.unmarshal(encodedDigests); err != nil {
|
|
|
|
return fmt.Errorf("unmarshalling yaml: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (d List) MarshalJSON() ([]byte, error) {
|
2023-01-18 10:49:55 -05:00
|
|
|
encodedIDKeyDigests := []string{}
|
|
|
|
for _, digest := range d {
|
|
|
|
encodedIDKeyDigests = append(encodedIDKeyDigests, hex.EncodeToString(digest))
|
|
|
|
}
|
|
|
|
return json.Marshal(encodedIDKeyDigests)
|
|
|
|
}
|
|
|
|
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (d *List) UnmarshalJSON(b []byte) error {
|
|
|
|
var encodedDigests encodedList
|
2023-01-18 10:49:55 -05:00
|
|
|
if err := json.Unmarshal(b, &encodedDigests); err != nil {
|
|
|
|
// Unmarshalling failed, IDKeyDigests might be a simple string instead of IDKeyDigests struct.
|
|
|
|
var unmarshalledString string
|
|
|
|
if legacyErr := json.Unmarshal(b, &unmarshalledString); legacyErr != nil {
|
2023-02-07 06:56:25 -05:00
|
|
|
return errors.Join(
|
2023-01-18 10:49:55 -05:00
|
|
|
err,
|
|
|
|
fmt.Errorf("trying legacy format: %w", legacyErr),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
encodedDigests = []string{unmarshalledString}
|
|
|
|
}
|
|
|
|
if err := d.unmarshal(encodedDigests); err != nil {
|
|
|
|
return fmt.Errorf("unmarshalling json: %w", err)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// unmarshal is a helper function for unmarshalling encodedIDKeyDigests into IDKeyDigests.
|
2023-04-06 11:00:56 -04:00
|
|
|
func (d *List) unmarshal(encodedDigests encodedList) error {
|
2023-01-18 10:49:55 -05:00
|
|
|
for _, encodedDigest := range encodedDigests {
|
|
|
|
if len(encodedDigest) != encodedDigestLength {
|
|
|
|
return fmt.Errorf("invalid digest length: %d", len(encodedDigest))
|
|
|
|
}
|
|
|
|
digest, err := hex.DecodeString(encodedDigest)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("decoding digest: %w", err)
|
|
|
|
}
|
|
|
|
*d = append(*d, digest)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|