blob: d2aa721b175edc02a6aac1f49184e322939e6b3b [file] [log] [blame]
Lorenz Bruna50e8452020-09-09 17:09:27 +02001// Copyright 2020 The Monogon Project Authors.
2//
3// SPDX-License-Identifier: Apache-2.0
4//
5// Licensed under the Apache License, Version 2.0 (the "License");
6// you may not use this file except in compliance with the License.
7// You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing, software
12// distributed under the License is distributed on an "AS IS" BASIS,
13// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14// See the License for the specific language governing permissions and
15// limitations under the License.
16
17// Taken and pruned from go-attestation under Apache 2.0
18package eventlog
19
20import (
21 "bytes"
22 "errors"
23 "fmt"
24
25 "github.com/google/certificate-transparency-go/x509"
26
Serge Bazanski31370b02021-01-07 16:31:14 +010027 "source.monogon.dev/metropolis/pkg/tpm/eventlog/internal"
Lorenz Bruna50e8452020-09-09 17:09:27 +020028)
29
30// SecurebootState describes the secure boot status of a machine, as determined
31// by processing its event log.
32type SecurebootState struct {
33 Enabled bool
34
35 // PlatformKeys enumerates keys which can sign a key exchange key.
36 PlatformKeys []x509.Certificate
37 // PlatformKeys enumerates key hashes which can sign a key exchange key.
38 PlatformKeyHashes [][]byte
39
40 // ExchangeKeys enumerates keys which can sign a database of permitted or
41 // forbidden keys.
42 ExchangeKeys []x509.Certificate
43 // ExchangeKeyHashes enumerates key hashes which can sign a database or
44 // permitted or forbidden keys.
45 ExchangeKeyHashes [][]byte
46
47 // PermittedKeys enumerates keys which may sign binaries to run.
48 PermittedKeys []x509.Certificate
49 // PermittedHashes enumerates hashes which permit binaries to run.
50 PermittedHashes [][]byte
51
52 // ForbiddenKeys enumerates keys which must not permit a binary to run.
53 ForbiddenKeys []x509.Certificate
54 // ForbiddenKeys enumerates hashes which must not permit a binary to run.
55 ForbiddenHashes [][]byte
56
57 // PreSeparatorAuthority describes the use of a secure-boot key to authorize
58 // the execution of a binary before the separator.
59 PreSeparatorAuthority []x509.Certificate
60 // PostSeparatorAuthority describes the use of a secure-boot key to authorize
61 // the execution of a binary after the separator.
62 PostSeparatorAuthority []x509.Certificate
63}
64
65// ParseSecurebootState parses a series of events to determine the
66// configuration of secure boot on a device. An error is returned if
67// the state cannot be determined, or if the event log is structured
68// in such a way that it may have been tampered post-execution of
69// platform firmware.
70func ParseSecurebootState(events []Event) (*SecurebootState, error) {
71 // This algorithm verifies the following:
72 // - All events in PCR 7 have event types which are expected in PCR 7.
73 // - All events are parsable according to their event type.
74 // - All events have digests values corresponding to their data/event type.
75 // - No unverifiable events were present.
76 // - All variables are specified before the separator and never duplicated.
77 // - The SecureBoot variable has a value of 0 or 1.
78 // - If SecureBoot was 1 (enabled), authority events were present indicating
79 // keys were used to perform verification.
80 // - If SecureBoot was 1 (enabled), platform + exchange + database keys
81 // were specified.
82 // - No UEFI debugger was attached.
83
84 var (
85 out SecurebootState
86 seenSeparator bool
87 seenAuthority bool
88 seenVars = map[string]bool{}
89 )
90
91 for _, e := range events {
92 if e.Index != 7 {
93 continue
94 }
95
96 et, err := internal.UntrustedParseEventType(uint32(e.Type))
97 if err != nil {
98 return nil, fmt.Errorf("unrecognised event type: %v", err)
99 }
100
101 digestVerify := e.digestEquals(e.Data)
102 switch et {
103 case internal.Separator:
104 if seenSeparator {
105 return nil, fmt.Errorf("duplicate separator at event %d", e.sequence)
106 }
107 seenSeparator = true
108 if !bytes.Equal(e.Data, []byte{0, 0, 0, 0}) {
109 return nil, fmt.Errorf("invalid separator data at event %d: %v", e.sequence, e.Data)
110 }
111 if digestVerify != nil {
112 return nil, fmt.Errorf("invalid separator digest at event %d: %v", e.sequence, digestVerify)
113 }
114
115 case internal.EFIAction:
116 if string(e.Data) == "UEFI Debug Mode" {
117 return nil, errors.New("a UEFI debugger was present during boot")
118 }
119 return nil, fmt.Errorf("event %d: unexpected EFI action event", e.sequence)
120
121 case internal.EFIVariableDriverConfig:
122 v, err := internal.ParseUEFIVariableData(bytes.NewReader(e.Data))
123 if err != nil {
124 return nil, fmt.Errorf("failed parsing EFI variable at event %d: %v", e.sequence, err)
125 }
126 if _, seenBefore := seenVars[v.VarName()]; seenBefore {
127 return nil, fmt.Errorf("duplicate EFI variable %q at event %d", v.VarName(), e.sequence)
128 }
129 seenVars[v.VarName()] = true
130 if seenSeparator {
131 return nil, fmt.Errorf("event %d: variable %q specified after separator", e.sequence, v.VarName())
132 }
133
134 if digestVerify != nil {
135 return nil, fmt.Errorf("invalid digest for variable %q on event %d: %v", v.VarName(), e.sequence, digestVerify)
136 }
137
138 switch v.VarName() {
139 case "SecureBoot":
140 if len(v.VariableData) != 1 {
141 return nil, fmt.Errorf("event %d: SecureBoot data len is %d, expected 1", e.sequence, len(v.VariableData))
142 }
143 out.Enabled = v.VariableData[0] == 1
144 case "PK":
145 if out.PlatformKeys, out.PlatformKeyHashes, err = v.SignatureData(); err != nil {
146 return nil, fmt.Errorf("event %d: failed parsing platform keys: %v", e.sequence, err)
147 }
148 case "KEK":
149 if out.ExchangeKeys, out.ExchangeKeyHashes, err = v.SignatureData(); err != nil {
150 return nil, fmt.Errorf("event %d: failed parsing key exchange keys: %v", e.sequence, err)
151 }
152 case "db":
153 if out.PermittedKeys, out.PermittedHashes, err = v.SignatureData(); err != nil {
154 return nil, fmt.Errorf("event %d: failed parsing signature database: %v", e.sequence, err)
155 }
156 case "dbx":
157 if out.ForbiddenKeys, out.ForbiddenHashes, err = v.SignatureData(); err != nil {
158 return nil, fmt.Errorf("event %d: failed parsing forbidden signature database: %v", e.sequence, err)
159 }
160 }
161
162 case internal.EFIVariableAuthority:
163 a, err := internal.ParseUEFIVariableAuthority(bytes.NewReader(e.Data))
164 if err != nil {
165 // Workaround for: https://github.com/google/go-attestation/issues/157
166 if err == internal.ErrSigMissingGUID {
167 // Versions of shim which do not carry
168 // https://github.com/rhboot/shim/commit/8a27a4809a6a2b40fb6a4049071bf96d6ad71b50
169 // have an erroneous additional byte in the event, which breaks digest
170 // verification. If verification failed, we try removing the last byte.
171 if digestVerify != nil {
172 digestVerify = e.digestEquals(e.Data[:len(e.Data)-1])
173 }
174 } else {
175 return nil, fmt.Errorf("failed parsing EFI variable authority at event %d: %v", e.sequence, err)
176 }
177 }
178 seenAuthority = true
179 if digestVerify != nil {
180 return nil, fmt.Errorf("invalid digest for authority on event %d: %v", e.sequence, digestVerify)
181 }
182 if !seenSeparator {
183 out.PreSeparatorAuthority = append(out.PreSeparatorAuthority, a.Certs...)
184 } else {
185 out.PostSeparatorAuthority = append(out.PostSeparatorAuthority, a.Certs...)
186 }
187
188 default:
189 return nil, fmt.Errorf("unexpected event type: %v", et)
190 }
191 }
192
193 if !out.Enabled {
194 return &out, nil
195 }
196
197 if !seenAuthority {
198 return nil, errors.New("secure boot was enabled but no key was used")
199 }
200 if len(out.PlatformKeys) == 0 && len(out.PlatformKeyHashes) == 0 {
201 return nil, errors.New("secure boot was enabled but no platform keys were known")
202 }
203 if len(out.ExchangeKeys) == 0 && len(out.ExchangeKeyHashes) == 0 {
204 return nil, errors.New("secure boot was enabled but no key exchange keys were known")
205 }
206 if len(out.PermittedKeys) == 0 && len(out.PermittedHashes) == 0 {
207 return nil, errors.New("secure boot was enabled but no keys or hashes were permitted")
208 }
209 return &out, nil
210}