talm

Форк
0
269 строк · 6.6 Кб
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
	"crypto/x509"
12
	"encoding/base64"
13
	"encoding/hex"
14
	"errors"
15
	"fmt"
16

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

20
	"github.com/aenix-io/talm/internal/pkg/secureboot"
21
	"github.com/siderolabs/talos/pkg/machinery/constants"
22
)
23

24
// Unseal unseals a sealed blob using the TPM
25
//
26
//nolint:gocyclo,cyclop
27
func Unseal(sealed SealedResponse) ([]byte, error) {
28
	t, err := transport.OpenTPM()
29
	if err != nil {
30
		return nil, err
31
	}
32
	defer t.Close() //nolint:errcheck
33

34
	// fail early if PCR banks are not present or filled with all zeroes or 0xff
35
	if err = validatePCRBanks(t); err != nil {
36
		return nil, err
37
	}
38

39
	tpmPub, err := tpm2.Unmarshal[tpm2.TPM2BPublic](sealed.SealedBlobPublic)
40
	if err != nil {
41
		return nil, err
42
	}
43

44
	tpmPriv, err := tpm2.Unmarshal[tpm2.TPM2BPrivate](sealed.SealedBlobPrivate)
45
	if err != nil {
46
		return nil, err
47
	}
48

49
	srk, err := tpm2.Unmarshal[tpm2.TPM2BName](sealed.KeyName)
50
	if err != nil {
51
		return nil, err
52
	}
53

54
	// we need to create a primary since we don't persist the SRK
55
	primary := tpm2.CreatePrimary{
56
		PrimaryHandle: tpm2.TPMRHOwner,
57
		InPublic:      tpm2.New2B(tpm2.ECCSRKTemplate),
58
	}
59

60
	createPrimaryResponse, err := primary.Execute(t)
61
	if err != nil {
62
		return nil, err
63
	}
64

65
	defer func() {
66
		flush := tpm2.FlushContext{
67
			FlushHandle: createPrimaryResponse.ObjectHandle,
68
		}
69

70
		_, flushErr := flush.Execute(t)
71
		if flushErr != nil {
72
			err = flushErr
73
		}
74
	}()
75

76
	outPub, err := createPrimaryResponse.OutPublic.Contents()
77
	if err != nil {
78
		return nil, err
79
	}
80

81
	if !bytes.Equal(createPrimaryResponse.Name.Buffer, srk.Buffer) {
82
		// this means the srk name does not match, possibly due to a different TPM or tpm was reset
83
		// could also mean the disk was used on a different machine
84
		return nil, errors.New("srk name does not match")
85
	}
86

87
	load := tpm2.Load{
88
		ParentHandle: tpm2.NamedHandle{
89
			Handle: createPrimaryResponse.ObjectHandle,
90
			Name:   createPrimaryResponse.Name,
91
		},
92
		InPrivate: *tpmPriv,
93
		InPublic:  *tpmPub,
94
	}
95

96
	loadResponse, err := load.Execute(t)
97
	if err != nil {
98
		return nil, err
99
	}
100

101
	policySess, policyCloseFunc, err := tpm2.PolicySession(
102
		t,
103
		tpm2.TPMAlgSHA256,
104
		20,
105
		tpm2.Salted(createPrimaryResponse.ObjectHandle, *outPub),
106
	)
107
	if err != nil {
108
		return nil, fmt.Errorf("failed to create policy session: %w", err)
109
	}
110

111
	defer policyCloseFunc() //nolint:errcheck
112

113
	pubKey, err := ParsePCRSigningPubKey(constants.PCRPublicKey)
114
	if err != nil {
115
		return nil, err
116
	}
117

118
	loadExternal := tpm2.LoadExternal{
119
		Hierarchy: tpm2.TPMRHOwner,
120
		InPublic:  tpm2.New2B(RSAPubKeyTemplate(pubKey.N.BitLen(), pubKey.E, pubKey.N.Bytes())),
121
	}
122

123
	loadExternalResponse, err := loadExternal.Execute(t)
124
	if err != nil {
125
		return nil, fmt.Errorf("failed to load external key: %w", err)
126
	}
127

128
	defer func() {
129
		flush := tpm2.FlushContext{
130
			FlushHandle: loadExternalResponse.ObjectHandle,
131
		}
132

133
		_, flushErr := flush.Execute(t)
134
		if flushErr != nil {
135
			err = flushErr
136
		}
137
	}()
138

139
	pcrSelector, err := CreateSelector([]int{secureboot.UKIPCR})
140
	if err != nil {
141
		return nil, err
142
	}
143

144
	policyDigest, err := PolicyPCRDigest(t, policySess.Handle(), tpm2.TPMLPCRSelection{
145
		PCRSelections: []tpm2.TPMSPCRSelection{
146
			{
147
				Hash:      tpm2.TPMAlgSHA256,
148
				PCRSelect: pcrSelector,
149
			},
150
		},
151
	})
152
	if err != nil {
153
		return nil, fmt.Errorf("failed to retrieve policy digest: %w", err)
154
	}
155

156
	sigJSON, err := ParsePCRSignature()
157
	if err != nil {
158
		return nil, err
159
	}
160

161
	pubKeyFingerprint := sha256.Sum256(x509.MarshalPKCS1PublicKey(pubKey))
162

163
	var signature string
164
	// TODO: maybe we should use the highest supported algorithm of the TPM
165
	// fallback to the next one if the signature is not found
166
	for _, bank := range sigJSON.SHA256 {
167
		digest, decodeErr := hex.DecodeString(bank.Pol)
168
		if decodeErr != nil {
169
			return nil, decodeErr
170
		}
171

172
		if bytes.Equal(digest, policyDigest.Buffer) {
173
			signature = bank.Sig
174

175
			if hex.EncodeToString(pubKeyFingerprint[:]) != bank.PKFP {
176
				return nil, errors.New("certificate fingerprint does not match")
177
			}
178

179
			break
180
		}
181
	}
182

183
	if signature == "" {
184
		return nil, errors.New("signature not found")
185
	}
186

187
	signatureDecoded, err := base64.StdEncoding.DecodeString(signature)
188
	if err != nil {
189
		return nil, err
190
	}
191

192
	// Verify will only verify the RSA part of the RSA+SHA256 signature,
193
	// hence we need to do the SHA256 part ourselves
194
	policyDigestHash := sha256.Sum256(policyDigest.Buffer)
195

196
	verifySignature := tpm2.VerifySignature{
197
		KeyHandle: loadExternalResponse.ObjectHandle,
198
		Digest: tpm2.TPM2BDigest{
199
			Buffer: policyDigestHash[:],
200
		},
201
		Signature: tpm2.TPMTSignature{
202
			SigAlg: tpm2.TPMAlgRSASSA,
203
			Signature: tpm2.NewTPMUSignature(tpm2.TPMAlgRSASSA, &tpm2.TPMSSignatureRSA{
204
				Hash: tpm2.TPMAlgSHA256,
205
				Sig: tpm2.TPM2BPublicKeyRSA{
206
					Buffer: signatureDecoded,
207
				},
208
			}),
209
		},
210
	}
211

212
	verifySignatureResponse, err := verifySignature.Execute(t)
213
	if err != nil {
214
		return nil, fmt.Errorf("failed to verify signature: %w", err)
215
	}
216

217
	policyAuthorize := tpm2.PolicyAuthorize{
218
		PolicySession:  policySess.Handle(),
219
		ApprovedPolicy: *policyDigest,
220
		KeySign:        loadExternalResponse.Name,
221
		CheckTicket:    verifySignatureResponse.Validation,
222
	}
223

224
	if _, err = policyAuthorize.Execute(t); err != nil {
225
		return nil, fmt.Errorf("failed to execute policy authorize: %w", err)
226
	}
227

228
	secureBootStatePCRSelector, err := CreateSelector([]int{secureboot.SecureBootStatePCR})
229
	if err != nil {
230
		return nil, err
231
	}
232

233
	secureBootStatePolicyDigest, err := PolicyPCRDigest(t, policySess.Handle(), tpm2.TPMLPCRSelection{
234
		PCRSelections: []tpm2.TPMSPCRSelection{
235
			{
236
				Hash:      tpm2.TPMAlgSHA256,
237
				PCRSelect: secureBootStatePCRSelector,
238
			},
239
		},
240
	})
241
	if err != nil {
242
		return nil, fmt.Errorf("failed to calculate policy PCR digest: %w", err)
243
	}
244

245
	if !bytes.Equal(secureBootStatePolicyDigest.Buffer, sealed.PolicyDigest) {
246
		return nil, errors.New("sealing policy digest does not match")
247
	}
248

249
	unsealOp := tpm2.Unseal{
250
		ItemHandle: tpm2.AuthHandle{
251
			Handle: loadResponse.ObjectHandle,
252
			Name:   loadResponse.Name,
253
			Auth:   policySess,
254
		},
255
	}
256

257
	unsealResponse, err := unsealOp.Execute(t, tpm2.HMAC(
258
		tpm2.TPMAlgSHA256,
259
		20,
260
		tpm2.Salted(createPrimaryResponse.ObjectHandle, *outPub),
261
		tpm2.AESEncryption(128, tpm2.EncryptOut),
262
		tpm2.Bound(loadResponse.ObjectHandle, loadResponse.Name, nil),
263
	))
264
	if err != nil {
265
		return nil, fmt.Errorf("failed to unseal op: %w", err)
266
	}
267

268
	return unsealResponse.OutData.Buffer, nil
269
}
270

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

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

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

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