blob: 4ce5696fbe82c346b47e1d4b6cb31e4edce5d452 [file] [log] [blame]
Tim Windelschmidt6d33a432025-02-04 14:34:25 +01001// Copyright The Monogon Project Authors.
Lorenz Bruna50e8452020-09-09 17:09:27 +02002// SPDX-License-Identifier: Apache-2.0
Lorenz Bruna50e8452020-09-09 17:09:27 +02003
4// Taken and pruned from go-attestation under Apache 2.0
5package eventlog
6
7import (
8 "bytes"
9 "errors"
10 "fmt"
11
12 "github.com/google/certificate-transparency-go/x509"
13
Tim Windelschmidt9f21f532024-05-07 15:14:20 +020014 "source.monogon.dev/osbase/tpm/eventlog/internal"
Lorenz Bruna50e8452020-09-09 17:09:27 +020015)
16
17// SecurebootState describes the secure boot status of a machine, as determined
18// by processing its event log.
19type SecurebootState struct {
20 Enabled bool
21
22 // PlatformKeys enumerates keys which can sign a key exchange key.
23 PlatformKeys []x509.Certificate
24 // PlatformKeys enumerates key hashes which can sign a key exchange key.
25 PlatformKeyHashes [][]byte
26
27 // ExchangeKeys enumerates keys which can sign a database of permitted or
28 // forbidden keys.
29 ExchangeKeys []x509.Certificate
30 // ExchangeKeyHashes enumerates key hashes which can sign a database or
31 // permitted or forbidden keys.
32 ExchangeKeyHashes [][]byte
33
34 // PermittedKeys enumerates keys which may sign binaries to run.
35 PermittedKeys []x509.Certificate
36 // PermittedHashes enumerates hashes which permit binaries to run.
37 PermittedHashes [][]byte
38
39 // ForbiddenKeys enumerates keys which must not permit a binary to run.
40 ForbiddenKeys []x509.Certificate
41 // ForbiddenKeys enumerates hashes which must not permit a binary to run.
42 ForbiddenHashes [][]byte
43
44 // PreSeparatorAuthority describes the use of a secure-boot key to authorize
45 // the execution of a binary before the separator.
46 PreSeparatorAuthority []x509.Certificate
47 // PostSeparatorAuthority describes the use of a secure-boot key to authorize
48 // the execution of a binary after the separator.
49 PostSeparatorAuthority []x509.Certificate
50}
51
52// ParseSecurebootState parses a series of events to determine the
53// configuration of secure boot on a device. An error is returned if
54// the state cannot be determined, or if the event log is structured
55// in such a way that it may have been tampered post-execution of
56// platform firmware.
57func ParseSecurebootState(events []Event) (*SecurebootState, error) {
58 // This algorithm verifies the following:
59 // - All events in PCR 7 have event types which are expected in PCR 7.
60 // - All events are parsable according to their event type.
61 // - All events have digests values corresponding to their data/event type.
62 // - No unverifiable events were present.
63 // - All variables are specified before the separator and never duplicated.
64 // - The SecureBoot variable has a value of 0 or 1.
65 // - If SecureBoot was 1 (enabled), authority events were present indicating
66 // keys were used to perform verification.
67 // - If SecureBoot was 1 (enabled), platform + exchange + database keys
68 // were specified.
69 // - No UEFI debugger was attached.
70
71 var (
72 out SecurebootState
73 seenSeparator bool
74 seenAuthority bool
75 seenVars = map[string]bool{}
76 )
77
78 for _, e := range events {
79 if e.Index != 7 {
80 continue
81 }
82
83 et, err := internal.UntrustedParseEventType(uint32(e.Type))
84 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020085 return nil, fmt.Errorf("unrecognised event type: %w", err)
Lorenz Bruna50e8452020-09-09 17:09:27 +020086 }
87
88 digestVerify := e.digestEquals(e.Data)
89 switch et {
90 case internal.Separator:
91 if seenSeparator {
92 return nil, fmt.Errorf("duplicate separator at event %d", e.sequence)
93 }
94 seenSeparator = true
95 if !bytes.Equal(e.Data, []byte{0, 0, 0, 0}) {
96 return nil, fmt.Errorf("invalid separator data at event %d: %v", e.sequence, e.Data)
97 }
98 if digestVerify != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +020099 return nil, fmt.Errorf("invalid separator digest at event %d: %w", e.sequence, digestVerify)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200100 }
101
102 case internal.EFIAction:
103 if string(e.Data) == "UEFI Debug Mode" {
104 return nil, errors.New("a UEFI debugger was present during boot")
105 }
106 return nil, fmt.Errorf("event %d: unexpected EFI action event", e.sequence)
107
108 case internal.EFIVariableDriverConfig:
109 v, err := internal.ParseUEFIVariableData(bytes.NewReader(e.Data))
110 if err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200111 return nil, fmt.Errorf("failed parsing EFI variable at event %d: %w", e.sequence, err)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200112 }
113 if _, seenBefore := seenVars[v.VarName()]; seenBefore {
114 return nil, fmt.Errorf("duplicate EFI variable %q at event %d", v.VarName(), e.sequence)
115 }
116 seenVars[v.VarName()] = true
117 if seenSeparator {
118 return nil, fmt.Errorf("event %d: variable %q specified after separator", e.sequence, v.VarName())
119 }
120
121 if digestVerify != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200122 return nil, fmt.Errorf("invalid digest for variable %q on event %d: %w", v.VarName(), e.sequence, digestVerify)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200123 }
124
125 switch v.VarName() {
126 case "SecureBoot":
127 if len(v.VariableData) != 1 {
128 return nil, fmt.Errorf("event %d: SecureBoot data len is %d, expected 1", e.sequence, len(v.VariableData))
129 }
130 out.Enabled = v.VariableData[0] == 1
131 case "PK":
132 if out.PlatformKeys, out.PlatformKeyHashes, err = v.SignatureData(); err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200133 return nil, fmt.Errorf("event %d: failed parsing platform keys: %w", e.sequence, err)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200134 }
135 case "KEK":
136 if out.ExchangeKeys, out.ExchangeKeyHashes, err = v.SignatureData(); err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200137 return nil, fmt.Errorf("event %d: failed parsing key exchange keys: %w", e.sequence, err)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200138 }
139 case "db":
140 if out.PermittedKeys, out.PermittedHashes, err = v.SignatureData(); err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200141 return nil, fmt.Errorf("event %d: failed parsing signature database: %w", e.sequence, err)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200142 }
143 case "dbx":
144 if out.ForbiddenKeys, out.ForbiddenHashes, err = v.SignatureData(); err != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200145 return nil, fmt.Errorf("event %d: failed parsing forbidden signature database: %w", e.sequence, err)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200146 }
147 }
148
149 case internal.EFIVariableAuthority:
150 a, err := internal.ParseUEFIVariableAuthority(bytes.NewReader(e.Data))
151 if err != nil {
152 // Workaround for: https://github.com/google/go-attestation/issues/157
Tim Windelschmidtd5f851b2024-04-23 14:59:37 +0200153 if errors.Is(err, internal.ErrSigMissingGUID) {
Lorenz Bruna50e8452020-09-09 17:09:27 +0200154 // Versions of shim which do not carry
155 // https://github.com/rhboot/shim/commit/8a27a4809a6a2b40fb6a4049071bf96d6ad71b50
156 // have an erroneous additional byte in the event, which breaks digest
157 // verification. If verification failed, we try removing the last byte.
158 if digestVerify != nil {
159 digestVerify = e.digestEquals(e.Data[:len(e.Data)-1])
160 }
161 } else {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200162 return nil, fmt.Errorf("failed parsing EFI variable authority at event %d: %w", e.sequence, err)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200163 }
164 }
165 seenAuthority = true
166 if digestVerify != nil {
Tim Windelschmidt5f1a7de2024-09-19 02:00:14 +0200167 return nil, fmt.Errorf("invalid digest for authority on event %d: %w", e.sequence, digestVerify)
Lorenz Bruna50e8452020-09-09 17:09:27 +0200168 }
169 if !seenSeparator {
170 out.PreSeparatorAuthority = append(out.PreSeparatorAuthority, a.Certs...)
171 } else {
172 out.PostSeparatorAuthority = append(out.PostSeparatorAuthority, a.Certs...)
173 }
174
175 default:
176 return nil, fmt.Errorf("unexpected event type: %v", et)
177 }
178 }
179
180 if !out.Enabled {
181 return &out, nil
182 }
183
184 if !seenAuthority {
185 return nil, errors.New("secure boot was enabled but no key was used")
186 }
187 if len(out.PlatformKeys) == 0 && len(out.PlatformKeyHashes) == 0 {
188 return nil, errors.New("secure boot was enabled but no platform keys were known")
189 }
190 if len(out.ExchangeKeys) == 0 && len(out.ExchangeKeyHashes) == 0 {
191 return nil, errors.New("secure boot was enabled but no key exchange keys were known")
192 }
193 if len(out.PermittedKeys) == 0 && len(out.PermittedHashes) == 0 {
194 return nil, errors.New("secure boot was enabled but no keys or hashes were permitted")
195 }
196 return &out, nil
197}