talm

Форк
0
194 строки · 4.7 Кб
1
// This Source Code Form is subject to the terms of the Mozilla Public
2
// License, v. 2.0. If a copy of the MPL was not distributed with this
3
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4

5
// Package tpm2 provides TPM2.0 related functionality helpers.
6
package tpm2
7

8
import (
9
	"bytes"
10
	"crypto/sha256"
11
	"fmt"
12
	"log"
13
	"os"
14
	"strings"
15

16
	"github.com/google/go-tpm/tpm2"
17
	"github.com/google/go-tpm/tpm2/transport"
18

19
	"github.com/aenix-io/talm/internal/pkg/secureboot"
20
)
21

22
// CreateSelector converts PCR  numbers into a bitmask.
23
func CreateSelector(pcrs []int) ([]byte, error) {
24
	const sizeOfPCRSelect = 3
25

26
	mask := make([]byte, sizeOfPCRSelect)
27

28
	for _, n := range pcrs {
29
		if n >= 8*sizeOfPCRSelect {
30
			return nil, fmt.Errorf("PCR index %d is out of range (exceeds maximum value %d)", n, 8*sizeOfPCRSelect-1)
31
		}
32

33
		mask[n>>3] |= 1 << (n & 0x7)
34
	}
35

36
	return mask, nil
37
}
38

39
// ReadPCR reads the value of a single PCR.
40
func ReadPCR(t transport.TPM, pcr int) ([]byte, error) {
41
	pcrSelector, err := CreateSelector([]int{pcr})
42
	if err != nil {
43
		return nil, fmt.Errorf("failed to create PCR selection: %w", err)
44
	}
45

46
	pcrRead := tpm2.PCRRead{
47
		PCRSelectionIn: tpm2.TPMLPCRSelection{
48
			PCRSelections: []tpm2.TPMSPCRSelection{
49
				{
50
					Hash:      tpm2.TPMAlgSHA256,
51
					PCRSelect: pcrSelector,
52
				},
53
			},
54
		},
55
	}
56

57
	pcrValue, err := pcrRead.Execute(t)
58
	if err != nil {
59
		return nil, fmt.Errorf("failed to read PCR: %w", err)
60
	}
61

62
	return pcrValue.PCRValues.Digests[0].Buffer, nil
63
}
64

65
// PCRExtent hashes the input and extends the PCR with the hash.
66
func PCRExtent(pcr int, data []byte) error {
67
	t, err := transport.OpenTPM()
68
	if err != nil {
69
		// if the TPM is not available or not a TPM 2.0, we can skip the PCR extension
70
		if os.IsNotExist(err) || strings.Contains(err.Error(), "device is not a TPM 2.0") {
71
			log.Printf("TPM device is not available, skipping PCR extension")
72

73
			return nil
74
		}
75

76
		return err
77
	}
78

79
	defer t.Close() //nolint:errcheck
80

81
	// since we are using SHA256, we can assume that the PCR bank is SHA256
82
	digest := sha256.Sum256(data)
83

84
	pcrHandle := tpm2.PCRExtend{
85
		PCRHandle: tpm2.AuthHandle{
86
			Handle: tpm2.TPMHandle(pcr),
87
			Auth:   tpm2.PasswordAuth(nil),
88
		},
89
		Digests: tpm2.TPMLDigestValues{
90
			Digests: []tpm2.TPMTHA{
91
				{
92
					HashAlg: tpm2.TPMAlgSHA256,
93
					Digest:  digest[:],
94
				},
95
			},
96
		},
97
	}
98

99
	if _, err = pcrHandle.Execute(t); err != nil {
100
		return err
101
	}
102

103
	return nil
104
}
105

106
// PolicyPCRDigest executes policyPCR and returns the digest.
107
func PolicyPCRDigest(t transport.TPM, policyHandle tpm2.TPMHandle, pcrSelection tpm2.TPMLPCRSelection) (*tpm2.TPM2BDigest, error) {
108
	policyPCR := tpm2.PolicyPCR{
109
		PolicySession: policyHandle,
110
		Pcrs:          pcrSelection,
111
	}
112

113
	if _, err := policyPCR.Execute(t); err != nil {
114
		return nil, fmt.Errorf("failed to execute policyPCR: %w", err)
115
	}
116

117
	policyGetDigest := tpm2.PolicyGetDigest{
118
		PolicySession: policyHandle,
119
	}
120

121
	policyGetDigestResponse, err := policyGetDigest.Execute(t)
122
	if err != nil {
123
		return nil, fmt.Errorf("failed to get policy digest: %w", err)
124
	}
125

126
	return &policyGetDigestResponse.PolicyDigest, nil
127
}
128

129
//nolint:gocyclo
130
func validatePCRBanks(t transport.TPM) error {
131
	pcrValue, err := ReadPCR(t, secureboot.UKIPCR)
132
	if err != nil {
133
		return fmt.Errorf("failed to read PCR: %w", err)
134
	}
135

136
	if err = validatePCRNotZeroAndNotFilled(pcrValue, secureboot.UKIPCR); err != nil {
137
		return err
138
	}
139

140
	pcrValue, err = ReadPCR(t, secureboot.SecureBootStatePCR)
141
	if err != nil {
142
		return fmt.Errorf("failed to read PCR: %w", err)
143
	}
144

145
	if err = validatePCRNotZeroAndNotFilled(pcrValue, secureboot.SecureBootStatePCR); err != nil {
146
		return err
147
	}
148

149
	caps := tpm2.GetCapability{
150
		Capability:    tpm2.TPMCapPCRs,
151
		Property:      0,
152
		PropertyCount: 1,
153
	}
154

155
	capsResp, err := caps.Execute(t)
156
	if err != nil {
157
		return fmt.Errorf("failed to get PCR capabilities: %w", err)
158
	}
159

160
	assignedPCRs, err := capsResp.CapabilityData.Data.AssignedPCR()
161
	if err != nil {
162
		return fmt.Errorf("failed to parse assigned PCRs: %w", err)
163
	}
164

165
	for _, s := range assignedPCRs.PCRSelections {
166
		if s.Hash != tpm2.TPMAlgSHA256 {
167
			continue
168
		}
169

170
		// check if 24 banks are available
171
		if len(s.PCRSelect) != 24/8 {
172
			return fmt.Errorf("unexpected number of PCR banks: %d", len(s.PCRSelect))
173
		}
174

175
		// check if all banks are available
176
		if s.PCRSelect[0] != 0xff || s.PCRSelect[1] != 0xff || s.PCRSelect[2] != 0xff {
177
			return fmt.Errorf("unexpected PCR banks: %v", s.PCRSelect)
178
		}
179
	}
180

181
	return nil
182
}
183

184
func validatePCRNotZeroAndNotFilled(pcrValue []byte, pcr int) error {
185
	if bytes.Equal(pcrValue, bytes.Repeat([]byte{0x00}, sha256.Size)) {
186
		return fmt.Errorf("PCR bank %d is populated with all zeroes", pcr)
187
	}
188

189
	if bytes.Equal(pcrValue, bytes.Repeat([]byte{0xFF}, sha256.Size)) {
190
		return fmt.Errorf("PCR bank %d is populated with all 0xFF", pcr)
191
	}
192

193
	return nil
194
}
195

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.