server/coscel/cos_tlv.go (94 lines of code) (raw):

// Package coscel contains the COS TLV event type and related functions. package coscel import ( "crypto" "fmt" "regexp" "strings" "unicode/utf8" "github.com/google/go-eventlog/cel" ) const ( // CELRType indicates the CELR event is a COS content // TODO: the value needs to be reserved in the CEL spec CELRType uint8 = 80 // EventPCRIndex is the PCR which should be used for CosEventType events. EventPCRIndex = 13 // EventRTMRIndex is the RTMR to be extended for COS events // According to https://uefi.org/specs/UEFI/2.10/38_Confidential_Computing.html // CCELMRIndex TDX Register // 0 MRTD // 1 RTMR[0] // 2 RTMR[1] // 3 RTMR[2] // So: // 4 RTMR[3] EventRTMRIndex = 3 // COSCCELMRIndex is the CCMR index to use in eventlog for COS events. COSCCELMRIndex = 4 ) // ContentType represent a COS content type in a CEL record content. type ContentType uint8 // Type for COS nested events const ( ImageRefType ContentType = iota ImageDigestType RestartPolicyType ImageIDType ArgType EnvVarType OverrideArgType OverrideEnvType LaunchSeparatorType MemoryMonitorType ) // COSTLV is a specific event type created for the COS (Google Container-Optimized OS), // used as a CEL content. type COSTLV struct { EventType ContentType EventContent []byte } // TLV returns the TLV representation of the COS TLV. func (c COSTLV) TLV() (cel.TLV, error) { data, err := cel.TLV{uint8(c.EventType), c.EventContent}.MarshalBinary() if err != nil { return cel.TLV{}, err } return cel.TLV{ Type: CELRType, Value: data, }, nil } // GenerateDigest generates the digest for the given COS TLV. The whole TLV struct will // be marshaled to bytes and feed into the hash algo. func (c COSTLV) GenerateDigest(hashAlgo crypto.Hash) ([]byte, error) { contentTLV, err := c.TLV() if err != nil { return nil, err } b, err := contentTLV.MarshalBinary() if err != nil { return nil, err } hash := hashAlgo.New() if _, err = hash.Write(b); err != nil { return nil, err } return hash.Sum(nil), nil } // ParseToCOSTLV constructs a CosTlv from t. It will check for the correct COS event // type, and unmarshal the nested event. func ParseToCOSTLV(t cel.TLV) (COSTLV, error) { if !IsCOSTLV(t) { return COSTLV{}, fmt.Errorf("TLV type %v is not a COS event", t.Type) } nestedEvent := cel.TLV{} err := nestedEvent.UnmarshalBinary(t.Value) if err != nil { return COSTLV{}, err } return COSTLV{ContentType(nestedEvent.Type), nestedEvent.Value}, nil } // IsCOSTLV check whether t is a COS TLV by its Type value. func IsCOSTLV(t cel.TLV) bool { return t.Type == CELRType } // FormatEnvVar takes in an environment variable name and its value, run some checks. Concats // the name and value by '=' and returns it if valid; returns an error if the name or value // is invalid. func FormatEnvVar(name string, value string) (string, error) { if !utf8.ValidString(name) { return "", fmt.Errorf("malformed env name, contains non-utf8 character: [%s]", name) } if !utf8.ValidString(value) { return "", fmt.Errorf("malformed env value, contains non-utf8 character: [%s]", value) } var envVarNameRegexp = regexp.MustCompile("^[a-zA-Z_][a-zA-Z0-9_]*$") if !envVarNameRegexp.MatchString(name) { return "", fmt.Errorf("malformed env name [%s], env name must start with an alpha character or '_', followed by a string of alphanumeric characters or '_' (%s)", name, envVarNameRegexp) } return name + "=" + value, nil } // ParseEnvVar takes in environment variable as a string (foo=bar), parses it and returns its name // and value, or an error if it fails the validation check. func ParseEnvVar(envvar string) (string, string, error) { // switch to strings.Cut when upgrading to go 1.18 e := strings.SplitN(string(envvar), "=", 2) if len(e) < 2 { return "", "", fmt.Errorf("malformed env var, doesn't contain '=': [%s]", envvar) } if _, err := FormatEnvVar(e[0], e[1]); err != nil { return "", "", err } return e[0], e[1], nil }