-
Notifications
You must be signed in to change notification settings - Fork 0
/
tpm.go
158 lines (133 loc) · 3.83 KB
/
tpm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
package fleet
import (
"crypto"
"crypto/x509"
"encoding/asn1"
"encoding/base64"
"encoding/binary"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"math/big"
"sync"
"time"
"github.com/google/go-tpm-tools/client"
"github.com/google/go-tpm/legacy/tpm2"
"github.com/google/go-tpm/tpmutil"
)
type tpmKey struct {
lk sync.Mutex
key *client.Key
}
var (
tpmKeyObject *tpmKey
tpmKeyInit sync.Mutex
tpmKeyOnce sync.Once
tpmConn io.ReadWriteCloser
tpmKeyTemplate = tpm2.Public{
Type: tpm2.AlgECC,
NameAlg: tpm2.AlgSHA256,
Attributes: tpm2.FlagSign | tpm2.FlagFixedTPM | tpm2.FlagFixedParent | tpm2.FlagSensitiveDataOrigin | tpm2.FlagUserWithAuth,
ECCParameters: &tpm2.ECCParams{
Symmetric: &tpm2.SymScheme{Alg: tpm2.AlgNull, KeyBits: 0, Mode: 0},
Sign: &tpm2.SigScheme{Alg: tpm2.AlgECDSA, Hash: tpm2.AlgSHA256},
CurveID: tpm2.CurveNISTP256,
},
}
)
// This struct is used to marshal and unmarshal an ECDSA signature,
// which consists of two big integers.
type ecdsaSignature struct {
R, S *big.Int
}
func (a *Agent) getTpmKey() (crypto.Signer, error) {
return getTpmKey()
}
func getTpmKey() (crypto.Signer, error) {
tpmKeyInit.Lock()
defer tpmKeyInit.Unlock()
if tpmKeyObject != nil {
return tpmKeyObject, nil
}
// the default paths on Linux (/dev/tpmrm0 then /dev/tpm0), will be used
var err error
if tpmConn == nil {
tpmConn, err = tpm2open()
if err != nil {
return nil, err
}
}
// only perform this after we got a successful connection to the tpm
handle := tpmutil.Handle(0x81010001)
var key *client.Key
key, err = client.NewCachedKey(tpmConn, tpm2.HandleOwner, tpmKeyTemplate, handle)
if err != nil {
return nil, err
}
tpmKeyObject = &tpmKey{
key: key,
}
slog.Info(fmt.Sprintf("instanciated tpm key: %s", tpmKeyObject.String()), "event", "fleet:tpm:init")
return tpmKeyObject, nil
}
func (k *tpmKey) Public() crypto.PublicKey {
return k.key.PublicKey()
}
func (k *tpmKey) String() string {
b, err := x509.MarshalPKIXPublicKey(k.Public())
if err != nil {
return fmt.Sprintf("INVALID KEY (%s)", err)
}
return base64.RawURLEncoding.EncodeToString(b)
}
func (k *tpmKey) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts) ([]byte, error) {
k.lk.Lock()
defer k.lk.Unlock()
// rand will be ignored because the tpm will do the signature
sig, err := tpm2.Sign(tpmConn, k.key.Handle(), "", digest, nil, &tpm2.SigScheme{Alg: tpm2.AlgECDSA, Hash: tpm2.AlgSHA256})
if err != nil {
return nil, err
}
// prepare a structure that can be marshalled by asn1
ecdsaSig := ecdsaSignature{
R: sig.ECC.R,
S: sig.ECC.S,
}
return asn1.Marshal(ecdsaSig)
}
func (k *tpmKey) Attest() ([]byte, error) {
// attempt to generate attestation
t := time.Now()
buf := make([]byte, 12)
binary.BigEndian.PutUint64(buf[:8], uint64(t.Unix()))
binary.BigEndian.PutUint32(buf[8:], uint32(t.Nanosecond()))
// grab public key
pubK := k.Public()
if pubK == nil {
return nil, errors.New("no public key")
}
pubB, err := x509.MarshalPKIXPublicKey(pubK)
if err != nil {
return nil, fmt.Errorf("while marshaling public key: %w", err)
}
nonce := buf // append(buf, pubB...)
_ = pubB
slog.Debug(fmt.Sprintf("preparing to attest nonce=%x", nonce), "event", "fleet:tpm:prep")
// prepare attestation
key, err := client.GceAttestationKeyECC(tpmConn)
if err != nil {
slog.Warn(fmt.Sprintf("[tpm] failed loading gce key, attempting standard attestation key..."), "event", "fleet:tpm:gce_fail")
key, err = client.AttestationKeyECC(tpmConn)
}
if err != nil {
slog.Error(fmt.Sprintf("[tpm] attestation key not available: %s", err), "event", "fleet:tpm:attest_fail")
return nil, fmt.Errorf("failed loading attestation key: %w", err)
}
res, err := key.Attest(client.AttestOpts{Nonce: nonce})
if err != nil {
return nil, fmt.Errorf("failed to attest: %w", err)
}
return json.Marshal(res)
}