forked from intel/trustauthority-client-for-go
-
Notifications
You must be signed in to change notification settings - Fork 0
/
token.go
267 lines (224 loc) · 7.66 KB
/
token.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
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
* Copyright (c) 2022-2023 Intel Corporation
* All rights reserved.
* SPDX-License-Identifier: BSD-3-Clause
*/
package connector
import (
"bytes"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/google/uuid"
"github.com/hashicorp/go-retryablehttp"
"github.com/lestrrat-go/jwx/v2/cert"
"github.com/lestrrat-go/jwx/v2/jwk"
"github.com/pkg/errors"
)
// tokenRequest holds all the data required for attestation
type tokenRequest struct {
Quote []byte `json:"quote"`
VerifierNonce *VerifierNonce `json:"verifier_nonce,omitempty"`
RuntimeData []byte `json:"runtime_data,omitempty"`
PolicyIds []uuid.UUID `json:"policy_ids,omitempty"`
EventLog []byte `json:"event_log,omitempty"`
}
// AttestationTokenResponse holds the token recieved from Intel Trust Authority
type AttestationTokenResponse struct {
Token string `json:"token"`
}
// GetToken is used to get attestation token from Intel Trust Authority
func (connector *trustAuthorityConnector) GetToken(args GetTokenArgs) (GetTokenResponse, error) {
url := fmt.Sprintf("%s/appraisal/v1/attest", connector.cfg.ApiUrl)
newRequest := func() (*http.Request, error) {
tr := tokenRequest{
Quote: args.Evidence.Evidence,
VerifierNonce: args.Nonce,
RuntimeData: args.Evidence.UserData,
PolicyIds: args.PolicyIds,
EventLog: args.Evidence.EventLog,
}
body, err := json.Marshal(tr)
if err != nil {
return nil, err
}
return http.NewRequest(http.MethodPost, url, bytes.NewReader(body))
}
var headers = map[string]string{
headerXApiKey: connector.cfg.ApiKey,
headerAccept: mimeApplicationJson,
headerContentType: mimeApplicationJson,
HeaderRequestId: args.RequestId,
}
var response GetTokenResponse
processResponse := func(resp *http.Response) error {
response.Headers = resp.Header
body, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Errorf("Failed to read body from %s: %s", url, err)
}
var tokenResponse AttestationTokenResponse
err = json.Unmarshal(body, &tokenResponse)
if err != nil {
return errors.Errorf("Error unmarshalling Token response from appraise: %s", err)
}
response.Token = tokenResponse.Token
return nil
}
if err := doRequest(*connector.rclient, connector.cfg.TlsCfg, newRequest, nil, headers, processResponse); err != nil {
return response, err
}
return response, nil
}
// getCRL is used to get CRL Object from CRL distribution points
func getCRL(rclient retryablehttp.Client, crlArr []string) (*x509.RevocationList, error) {
if len(crlArr) < 1 {
return nil, errors.New("Invalid CDP count present in the certificate")
}
_, err := url.Parse(crlArr[0])
if err != nil {
return nil, errors.Wrap(err, "Invalid CRL distribution point")
}
newRequest := func() (*http.Request, error) {
return http.NewRequest(http.MethodGet, crlArr[0], nil)
}
var crlObj *x509.RevocationList
processResponse := func(resp *http.Response) error {
crlBytes, err := io.ReadAll(resp.Body)
if err != nil {
return errors.Wrapf(err, "Failed to read body from %s", crlArr[0])
}
crlObj, err = x509.ParseRevocationList([]byte(crlBytes))
if err != nil {
return errors.Wrap(err, "Failed to parse revocation list")
}
return nil
}
tlsConfig := &tls.Config{
InsecureSkipVerify: false,
MinVersion: tls.VersionTLS12,
}
if err := doRequest(rclient, tlsConfig, newRequest, nil, nil, processResponse); err != nil {
return nil, err
}
return crlObj, nil
}
// verifyCRL is used to verify the Certificate against CRL
func verifyCRL(crl *x509.RevocationList, leafCert *x509.Certificate, caCert *x509.Certificate) error {
if leafCert == nil || caCert == nil || crl == nil {
return errors.New("Leaf Cert or CA Cert or CRL is nil")
}
//Checking CRL signed by CA Certificate
err := crl.CheckSignatureFrom(caCert)
if err != nil {
return errors.Wrap(err, "CRL signature verification failed")
}
if crl.NextUpdate.Before(time.Now()) {
return errors.New("Outdated CRL")
}
for _, rCert := range crl.RevokedCertificates {
if rCert.SerialNumber.Cmp(leafCert.SerialNumber) == 0 {
return errors.New("Certificate was Revoked")
}
}
return nil
}
// VerifyToken is used to do signature verification of attestation token recieved from Intel Trust Authority
func (connector *trustAuthorityConnector) VerifyToken(token string) (*jwt.Token, error) {
parsedToken, err := jwt.Parse(token, func(token *jwt.Token) (interface{}, error) {
var kid string
keyIDValue, keyIDExists := token.Header["kid"]
if !keyIDExists {
return nil, errors.New("kid field missing in token header")
} else {
var ok bool
kid, ok = keyIDValue.(string)
if !ok {
return nil, errors.Errorf("kid field in jwt header is not a valid string: %v", kid)
}
}
// Get the JWT Signing Certificates from Intel Trust Authority
jwks, err := connector.GetTokenSigningCertificates()
if err != nil {
return nil, errors.Errorf("Failed to get token signing certificates: %s", err)
}
// Unmarshal the JWKS
jwkSet, err := jwk.Parse(jwks)
if err != nil {
return nil, errors.Errorf("Unable to unmarshal response into a JWT Key Set: %s", err)
}
jwkKey, found := jwkSet.LookupKeyID(kid)
if !found {
return nil, errors.New("Could not find Key matching the key id")
}
// Verify the cert chain. x5c field in the JWKS would contain the cert chain
atsCerts := jwkKey.X509CertChain()
if atsCerts.Len() > AtsCertChainMaxLen {
return nil, errors.Errorf("Token Signing Cert chain has more than %d certificates", AtsCertChainMaxLen)
}
root := x509.NewCertPool()
intermediate := x509.NewCertPool()
var leafCert *x509.Certificate
var interCACert *x509.Certificate
var rootCert *x509.Certificate
for i := 0; i < atsCerts.Len(); i++ {
atsCert, ok := atsCerts.Get(i)
if !ok {
return nil, errors.Errorf("Failed to fetch certificate at index %d", i)
}
cer, err := cert.Parse(atsCert)
if err != nil {
return nil, errors.Errorf("Failed to parse x509 certificate[%d]: %v", i, err)
}
if cer.IsCA && cer.BasicConstraintsValid && strings.Contains(cer.Subject.CommonName, "Root CA") {
root.AddCert(cer)
rootCert = cer
} else if strings.Contains(cer.Subject.CommonName, "Signing CA") {
intermediate.AddCert(cer)
interCACert = cer
} else {
leafCert = cer
}
}
rootCrl, err := getCRL(*connector.rclient, interCACert.CRLDistributionPoints)
if err != nil {
return nil, errors.Errorf("Failed to get ROOT CA CRL Object: %v", err.Error())
}
if err = verifyCRL(rootCrl, interCACert, rootCert); err != nil {
return nil, errors.Errorf("Failed to check ATS CA Certificate against Root CA CRL: %v", err.Error())
}
atsCrl, err := getCRL(*connector.rclient, leafCert.CRLDistributionPoints)
if err != nil {
return nil, errors.Errorf("Failed to get ATS CRL Object: %v", err.Error())
}
if err = verifyCRL(atsCrl, leafCert, interCACert); err != nil {
return nil, errors.Errorf("Failed to check ATS Leaf certificate against ATS CRL: %v", err.Error())
}
// Verify the Leaf certificate against the CA
opts := x509.VerifyOptions{
Roots: root,
Intermediates: intermediate,
}
if _, err := leafCert.Verify(opts); err != nil {
return nil, errors.Errorf("Failed to verify cert chain: %v", err)
}
// Extract the public key from JWK using exponent and modulus
var pubKey interface{}
err = jwkKey.Raw(&pubKey)
if err != nil {
return nil, errors.Errorf("Failed to extract Public Key from Certificate: %s", err)
}
return pubKey, nil
})
if err != nil {
return nil, errors.Errorf("Failed to verify jwt token: %s", err)
}
return parsedToken, nil
}