blob: 849756260b4d1e175b9957c8bc5a4368945b2b52 [file] [log] [blame]
Lorenz Brunaa6b7342019-12-12 02:55:02 +01001// 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
17package tpm2
18
19import (
20 "context"
21 "errors"
22 "fmt"
23
24 "git.monogon.dev/source/nexantic.git/core/generated/api"
25 "git.monogon.dev/source/nexantic.git/core/internal/integrity"
26 "git.monogon.dev/source/nexantic.git/core/pkg/tpm"
27)
28
29type TPM2Agent struct {
30}
31
32func (a *TPM2Agent) Initialize(newNode api.NewNodeInfo, enrolment api.EnrolmentConfig) error {
33 nmsConn, err := integrity.DialNMS(enrolment)
34 nmsClient := api.NewNodeManagementServiceClient(nmsConn)
35 ekPub, ekCert, err := tpm.GetEKPublic()
36 if err != nil {
37 return fmt.Errorf("failed to generate EK: %w", err)
38 }
39
40 akPub, err := tpm.GetAKPublic()
41 if err != nil {
42 return fmt.Errorf("failed to generate AK: %w", err)
43 }
44
45 registerSession, err := nmsClient.NewTPM2NodeRegister(context.Background())
46 if err != nil {
47 return fmt.Errorf("failed to open registration session: %w", err)
48 }
49 defer registerSession.CloseSend()
50 if err := registerSession.Send(&api.TPM2FlowRequest{
51 Stage: &api.TPM2FlowRequest_Register{
52 Register: &api.TPM2RegisterRequest{
53 AkPublic: akPub,
54 EkPubkey: ekPub,
55 EkCert: ekCert,
56 },
57 },
58 }); err != nil {
59 return fmt.Errorf("failed to send registration: %w", err)
60 }
61
62 res1, err := registerSession.Recv()
63 if err != nil {
64 return fmt.Errorf("failed to receive attest request: %w", err)
65 }
66 attestReqContainer, ok := res1.Stage.(*api.TPM2FlowResponse_AttestRequest)
67 if !ok {
68 return errors.New("protocol violation: after RegisterRequest expected AttestRequest")
69 }
70 attestReq := attestReqContainer.AttestRequest
71 solution, err := tpm.SolveAKChallenge(attestReq.AkChallenge, attestReq.AkChallengeSecret)
72 if err != nil {
73 return fmt.Errorf("failed to solve AK challenge: %w", err)
74 }
75 pcrs, err := tpm.GetPCRs()
76 if err != nil {
77 return fmt.Errorf("failed to get SRTM PCRs: %w", err)
78 }
79 quote, quoteSig, err := tpm.AttestPlatform(attestReq.QuoteNonce)
80 if err != nil {
81 return fmt.Errorf("failed Quote operation: %w", err)
82 }
83 if err := registerSession.Send(&api.TPM2FlowRequest{
84 Stage: &api.TPM2FlowRequest_AttestResponse{
85 AttestResponse: &api.TPM2AttestResponse{
86 AkChallengeSolution: solution,
87 Pcrs: pcrs,
88 Quote: quote,
89 QuoteSignature: quoteSig,
90 },
91 },
92 }); err != nil {
93 return fmt.Errorf("failed to send AttestResponse: %w", err)
94 }
95 if err := registerSession.Send(&api.TPM2FlowRequest{
96 Stage: &api.TPM2FlowRequest_NewNodeInfo{
97 NewNodeInfo: &newNode,
98 },
99 }); err != nil {
100 return fmt.Errorf("failed to send NewNodeInfo: %w", err)
101 }
102 return nil
103}
104
105// Unlock attests the node state to the remote NMS and asks it for the global unlock key
106func (a *TPM2Agent) Unlock(enrolment api.EnrolmentConfig) ([]byte, error) {
107 nmsConn, err := integrity.DialNMS(enrolment)
108 if err != nil {
109 return []byte{}, err
110 }
111 nmsClient := api.NewNodeManagementServiceClient(nmsConn)
112 unlockClient, err := nmsClient.TPM2Unlock(context.Background())
113 if err != nil {
114 return []byte{}, err
115 }
116 defer unlockClient.CloseSend()
117 unlockInitContainer, err := unlockClient.Recv()
118 if err != nil {
119 return []byte{}, err
120 }
121 unlockInitVariant, ok := unlockInitContainer.Stage.(*api.TPM2UnlockFlowResponse_UnlockInit)
122 if !ok {
123 return []byte{}, errors.New("TPM2Unlock protocol violation")
124 }
125 unlockInit := unlockInitVariant.UnlockInit
126 quote, sig, err := tpm.AttestPlatform(unlockInit.Nonce)
127 if err != nil {
128 return []byte{}, fmt.Errorf("failed to attest platform: %w", err)
129 }
130 pcrs, err := tpm.GetPCRs()
131 if err != nil {
132 return []byte{}, fmt.Errorf("failed to get PCRs from TPM: %w", err)
133 }
134 if err := unlockClient.Send(&api.TPM2UnlockFlowRequeset{Stage: &api.TPM2UnlockFlowRequeset_UnlockRequest{
135 UnlockRequest: &api.TPM2UnlockRequest{
136 Pcrs: pcrs,
137 Quote: quote,
138 QuoteSignature: sig,
139 NodeId: enrolment.NodeId,
140 },
141 }}); err != nil {
142 return []byte{}, err
143 }
144 unlockResponseContainer, err := unlockClient.Recv()
145 if err != nil {
146 return []byte{}, err
147 }
148 unlockResponseVariant, ok := unlockResponseContainer.Stage.(*api.TPM2UnlockFlowResponse_UnlockResponse)
149 if !ok {
150 return []byte{}, errors.New("violated TPM2Unlock protocol")
151 }
152 unlockResponse := unlockResponseVariant.UnlockResponse
153
154 return unlockResponse.GlobalUnlockKey, nil
155}