2022-09-05 03:06:08 -04:00
|
|
|
/*
|
|
|
|
Copyright (c) Edgeless Systems GmbH
|
|
|
|
|
|
|
|
SPDX-License-Identifier: AGPL-3.0-only
|
|
|
|
*/
|
|
|
|
|
2022-05-12 04:15:00 -04:00
|
|
|
package config
|
|
|
|
|
2022-05-16 12:54:25 -04:00
|
|
|
import (
|
2022-08-01 03:37:05 -04:00
|
|
|
"bytes"
|
|
|
|
"context"
|
2022-10-11 07:57:52 -04:00
|
|
|
"crypto/sha256"
|
2022-05-16 12:54:25 -04:00
|
|
|
"encoding/base64"
|
2022-10-11 07:57:52 -04:00
|
|
|
"encoding/hex"
|
2022-08-01 03:37:05 -04:00
|
|
|
"fmt"
|
2022-08-05 09:30:23 -04:00
|
|
|
"io"
|
2022-08-01 03:37:05 -04:00
|
|
|
"net/http"
|
|
|
|
"net/url"
|
2022-05-16 12:54:25 -04:00
|
|
|
|
2022-09-21 07:47:57 -04:00
|
|
|
"github.com/edgelesssys/constellation/v2/internal/attestation/vtpm"
|
|
|
|
"github.com/edgelesssys/constellation/v2/internal/sigstore"
|
2022-08-01 03:37:05 -04:00
|
|
|
"gopkg.in/yaml.v2"
|
2022-05-16 12:54:25 -04:00
|
|
|
)
|
2022-05-12 04:15:00 -04:00
|
|
|
|
|
|
|
type Measurements map[uint32][]byte
|
|
|
|
|
2022-05-16 12:54:25 -04:00
|
|
|
var (
|
2022-08-30 09:34:33 -04:00
|
|
|
// gcpPCRs are the PCR values for a GCP Constellation node that are initially set in a generated config file.
|
2022-05-16 12:54:25 -04:00
|
|
|
gcpPCRs = Measurements{
|
|
|
|
0: {0x0F, 0x35, 0xC2, 0x14, 0x60, 0x8D, 0x93, 0xC7, 0xA6, 0xE6, 0x8A, 0xE7, 0x35, 0x9B, 0x4A, 0x8B, 0xE5, 0xA0, 0xE9, 0x9E, 0xEA, 0x91, 0x07, 0xEC, 0xE4, 0x27, 0xC4, 0xDE, 0xA4, 0xE4, 0x39, 0xCF},
|
|
|
|
uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
}
|
|
|
|
|
2022-08-30 09:34:33 -04:00
|
|
|
// azurePCRs are the PCR values for an Azure Constellation node that are initially set in a generated config file.
|
2022-05-16 12:54:25 -04:00
|
|
|
azurePCRs = Measurements{
|
|
|
|
uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
}
|
|
|
|
|
|
|
|
qemuPCRs = Measurements{
|
|
|
|
uint32(vtpm.PCRIndexOwnerID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
uint32(vtpm.PCRIndexClusterID): {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
2022-08-01 03:37:05 -04:00
|
|
|
// FetchAndVerify fetches measurement and signature files via provided URLs,
|
|
|
|
// using client for download. The publicKey is used to verify the measurements.
|
2022-10-11 07:57:52 -04:00
|
|
|
// The hash of the fetched measurements is returned.
|
|
|
|
func (m *Measurements) FetchAndVerify(ctx context.Context, client *http.Client, measurementsURL *url.URL, signatureURL *url.URL, publicKey []byte) (string, error) {
|
2022-08-01 03:37:05 -04:00
|
|
|
measurements, err := getFromURL(ctx, client, measurementsURL)
|
|
|
|
if err != nil {
|
2022-10-11 07:57:52 -04:00
|
|
|
return "", fmt.Errorf("failed to fetch measurements: %w", err)
|
2022-08-01 03:37:05 -04:00
|
|
|
}
|
|
|
|
signature, err := getFromURL(ctx, client, signatureURL)
|
|
|
|
if err != nil {
|
2022-10-11 07:57:52 -04:00
|
|
|
return "", fmt.Errorf("failed to fetch signature: %w", err)
|
2022-08-01 03:37:05 -04:00
|
|
|
}
|
|
|
|
if err := sigstore.VerifySignature(measurements, signature, publicKey); err != nil {
|
2022-10-11 07:57:52 -04:00
|
|
|
return "", err
|
2022-08-01 03:37:05 -04:00
|
|
|
}
|
|
|
|
if err := yaml.NewDecoder(bytes.NewReader(measurements)).Decode(&m); err != nil {
|
2022-10-11 07:57:52 -04:00
|
|
|
return "", err
|
2022-08-01 03:37:05 -04:00
|
|
|
}
|
2022-10-11 07:57:52 -04:00
|
|
|
|
|
|
|
shaHash := sha256.Sum256(measurements)
|
|
|
|
|
|
|
|
return hex.EncodeToString(shaHash[:]), nil
|
2022-08-01 03:37:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// CopyFrom copies over all values from other. Overwriting existing values,
|
|
|
|
// but keeping not specified values untouched.
|
|
|
|
func (m Measurements) CopyFrom(other Measurements) {
|
|
|
|
for idx := range other {
|
|
|
|
m[idx] = other[idx]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// MarshalYAML overwrites the default behaviour of writing out []byte not as
|
|
|
|
// single bytes, but as a single base64 encoded string.
|
2022-05-12 04:15:00 -04:00
|
|
|
func (m Measurements) MarshalYAML() (interface{}, error) {
|
|
|
|
base64Map := make(map[uint32]string)
|
|
|
|
|
|
|
|
for key, value := range m {
|
|
|
|
base64Map[key] = base64.StdEncoding.EncodeToString(value[:])
|
|
|
|
}
|
|
|
|
|
|
|
|
return base64Map, nil
|
|
|
|
}
|
|
|
|
|
2022-08-01 03:37:05 -04:00
|
|
|
// UnmarshalYAML overwrites the default behaviour of reading []byte not as
|
|
|
|
// single bytes, but as a single base64 encoded string.
|
2022-05-12 04:15:00 -04:00
|
|
|
func (m *Measurements) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
|
|
|
base64Map := make(map[uint32]string)
|
|
|
|
err := unmarshal(base64Map)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
*m = make(Measurements)
|
|
|
|
for key, value := range base64Map {
|
|
|
|
measurement, err := base64.StdEncoding.DecodeString(value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
(*m)[key] = measurement
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
2022-08-01 03:37:05 -04:00
|
|
|
|
|
|
|
func getFromURL(ctx context.Context, client *http.Client, sourceURL *url.URL) ([]byte, error) {
|
|
|
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, sourceURL.String(), http.NoBody)
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
|
|
|
|
resp, err := client.Do(req)
|
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
|
|
return []byte{}, fmt.Errorf("http status code: %d", resp.StatusCode)
|
|
|
|
}
|
2022-08-05 09:30:23 -04:00
|
|
|
content, err := io.ReadAll(resp.Body)
|
2022-08-01 03:37:05 -04:00
|
|
|
if err != nil {
|
|
|
|
return []byte{}, err
|
|
|
|
}
|
|
|
|
return content, nil
|
|
|
|
}
|