-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathvalidator.go
More file actions
93 lines (81 loc) · 2.99 KB
/
Copy pathvalidator.go
File metadata and controls
93 lines (81 loc) · 2.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
// Copyright 2025 Edgeless Systems GmbH
// SPDX-License-Identifier: BUSL-1.1
package validator
import (
"errors"
"fmt"
"slices"
snpclient "github.com/google/go-sev-guest/client"
tdxclient "github.com/google/go-tdx-guest/client"
)
// Validator compares an incoming initdata hash with HOSTDATA or MRCONFIGID, depending in the platform.
//
// Instances need to be created with New.
type Validator struct {
digestGetter digestGetter
}
// New constructs a Validator suitable for the current runtime environment, if available.
func New() (*Validator, error) {
sqp, serr := getSNPQuoteProvider()
if serr == nil {
return &Validator{&snpDigestGetter{sqp}}, nil
}
tqp, terr := getTDXQuoteProvider()
if terr == nil {
return &Validator{&tdxDigestGetter{tqp}}, nil
}
return nil, fmt.Errorf("%w:\nTDX:%w\nSNP:%w", ErrNoPlatform, terr, serr)
}
// ValidateDigest compares the given digest with either MRCONFIGID or HOSTDATA, and returns an error if they don't match.
//
// The minimum size of the digest argument is 32 bytes, corresponding to a SHA256 hash.
// The comparison matches the implementation in the Kata runtime with respect to truncation and
// padding:
// - https://github.com/kata-containers/kata-containers/blob/0d58bad/src/runtime/pkg/govmm/qemu/qemu.go#L427
// - https://github.com/kata-containers/kata-containers/blob/0d58bad/src/runtime/pkg/govmm/qemu/qemu.go#L516
func (v *Validator) ValidateDigest(digest []byte) error {
expectedDigest, err := v.digestGetter.GetDigest()
if err != nil {
return err
}
return compareDigests(expectedDigest, digest)
}
type digestGetter interface {
GetDigest() ([]byte, error)
}
func compareDigests(expected, actual []byte) error {
// minDigestLength is the minimum length of a digest.
// We check the length here just for completeness. The intended use of this function is to
// compare initdata digests (which are sha256, sha384 or sha512) with either HOSTDATA (32
// bytes) or MRCONFIGID (48 bytes), so all byte slices should have a minimum size of 32
// anyway.
const minDigestLength = 32
n := min(len(expected), len(actual))
if n < minDigestLength {
return fmt.Errorf("%w: expected %x, got %x", errUnexpectedDigestSize, expected, actual)
}
if slices.Compare(expected[:n], actual[:n]) != 0 {
return fmt.Errorf("%w: expected %x, got %x", errDigestMismatch, expected, actual)
}
return nil
}
func getSNPQuoteProvider() (snpclient.QuoteProvider, error) {
// snpclient checks IsSupported internally.
return snpclient.GetQuoteProvider()
}
func getTDXQuoteProvider() (tdxclient.QuoteProvider, error) {
tqp, err := tdxclient.GetQuoteProvider()
if err != nil {
return nil, err
}
if err := tqp.IsSupported(); err != nil {
return nil, err
}
return tqp, nil
}
var (
// ErrNoPlatform is returned by New when no TEE platform is available for digest validation.
ErrNoPlatform = errors.New("no digest getter available for current platform")
errUnexpectedDigestSize = errors.New("unexpected digest size")
errDigestMismatch = errors.New("digests don't match")
)