Skip to content

Commit 04b0cdf

Browse files
jovan-djukicJovan Djukic
andauthored
COCOS-492: Cache VCEK on aTLS verification (#524)
* initial commit * made changes based on errors * remove unnecessary log --------- Co-authored-by: Jovan Djukic <[email protected]>
1 parent 6b26f40 commit 04b0cdf

File tree

3 files changed

+128
-7
lines changed

3 files changed

+128
-7
lines changed

cmd/agent/main.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import (
2929
"github.com/ultravioletrs/cocos/pkg/atls"
3030
"github.com/ultravioletrs/cocos/pkg/attestation"
3131
"github.com/ultravioletrs/cocos/pkg/attestation/azure"
32+
"github.com/ultravioletrs/cocos/pkg/attestation/quoteprovider"
3233
"github.com/ultravioletrs/cocos/pkg/attestation/tdx"
3334
"github.com/ultravioletrs/cocos/pkg/attestation/vtpm"
3435
"github.com/ultravioletrs/cocos/pkg/clients"
@@ -155,6 +156,13 @@ func main() {
155156
return
156157
}
157158

159+
err = quoteprovider.FetchCertificates(uint(cfg.Vmpl))
160+
if err != nil {
161+
logger.Error(fmt.Sprintf("failed to fetch certificates: %s", err))
162+
exitCode = 1
163+
return
164+
}
165+
158166
svc := newService(ctx, logger, eventSvc, provider, cfg.Vmpl)
159167

160168
if err := os.MkdirAll(storageDir, 0o755); err != nil {

pkg/attestation/quoteprovider/sev.go

Lines changed: 119 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,14 @@
77
package quoteprovider
88

99
import (
10+
"crypto/rand"
11+
"crypto/x509"
12+
"encoding/pem"
1013
"fmt"
1114
"io"
1215
"os"
1316
"path"
17+
"path/filepath"
1418
"time"
1519

1620
"github.com/absmach/supermq/pkg/errors"
@@ -22,11 +26,13 @@ import (
2226
"github.com/google/go-sev-guest/verify/trust"
2327
"github.com/google/logger"
2428
"github.com/ultravioletrs/cocos/pkg/attestation"
29+
"google.golang.org/protobuf/encoding/protojson"
2530
)
2631

2732
const (
28-
cocosDirectory = ".cocos"
29-
caBundleName = "ask_ark.pem"
33+
cocosDirectory = "/cocos"
34+
arkAskBundleName = "ask_ark.pem"
35+
vcekName = "vcek.pem"
3036
Nonce = 64
3137
sevSnpProductMilan = "Milan"
3238
sevSnpProductGenoa = "Genoa"
@@ -57,7 +63,7 @@ func fillInAttestationLocal(attestation *sevsnp.Attestation, cfg *check.Config)
5763
return err
5864
}
5965

60-
bundlePath := path.Join(homePath, cocosDirectory, product, caBundleName)
66+
bundlePath := path.Join(homePath, cocosDirectory, product, arkAskBundleName)
6167
if _, err := os.Stat(bundlePath); err == nil {
6268
amdRootCerts := trust.AMDRootCerts{}
6369
if err := amdRootCerts.FromKDSCert(bundlePath); err != nil {
@@ -166,12 +172,39 @@ func FetchAttestation(reportDataSlice []byte, vmpl uint) ([]byte, error) {
166172
}
167173
copy(reportData[:], reportDataSlice)
168174

169-
rawQuote, err := qp.GetRawQuoteAtLevel(reportData, vmpl)
175+
quoteProto, err := client.GetQuoteProtoAtLevel(qp, reportData, vmpl)
170176
if err != nil {
171-
return []byte{}, fmt.Errorf("failed to get raw quote")
177+
return []byte{}, fmt.Errorf("failed to get quote proto")
172178
}
173179

174-
return rawQuote, nil
180+
homePath, _ := os.UserHomeDir()
181+
vcekPath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), vcekName)
182+
arkAskBundlePath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), arkAskBundleName)
183+
184+
vcekBytes, err := os.ReadFile(vcekPath)
185+
if err != nil {
186+
return []byte{}, fmt.Errorf("could not read VCEK file: %v", err)
187+
}
188+
189+
arkAskBundleBytes, err := os.ReadFile(arkAskBundlePath)
190+
if err != nil {
191+
return []byte{}, fmt.Errorf("could not read ask/ark bundle file: %v", err)
192+
}
193+
194+
vcekPem, _ := pem.Decode(vcekBytes)
195+
arkPem, rest := pem.Decode(arkAskBundleBytes)
196+
askPem, _ := pem.Decode(rest)
197+
198+
quoteProto.CertificateChain.VcekCert = vcekPem.Bytes
199+
quoteProto.CertificateChain.AskCert = askPem.Bytes
200+
quoteProto.CertificateChain.ArkCert = arkPem.Bytes
201+
202+
result, err := protojson.Marshal(quoteProto)
203+
if err != nil {
204+
return []byte{}, fmt.Errorf("failed to marshal quote proto: %v", err)
205+
}
206+
207+
return result, nil
175208
}
176209

177210
func GetProductName(product string) sevsnp.SevProduct_SevProductName {
@@ -184,3 +217,83 @@ func GetProductName(product string) sevsnp.SevProduct_SevProductName {
184217
return sevsnp.SevProduct_SEV_PRODUCT_UNKNOWN
185218
}
186219
}
220+
221+
func derToPem(der []byte) []byte {
222+
// Try to parse to make sure it's a certificate
223+
if _, err := x509.ParseCertificate(der); err != nil {
224+
// cert_chain endpoint already returns PEM; just pass through
225+
return der
226+
}
227+
return pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
228+
}
229+
230+
func FetchCertificates(vmpl uint) error {
231+
var reportData [Nonce]byte
232+
233+
qp, err := GetLeveledQuoteProvider()
234+
if err != nil {
235+
return fmt.Errorf("could not get quote provider")
236+
}
237+
238+
if len(reportData) > Nonce {
239+
return fmt.Errorf("attestation report size mismatch")
240+
}
241+
242+
_, err = rand.Read(reportData[:])
243+
if err != nil {
244+
return fmt.Errorf("failed to read random data: %v", err)
245+
}
246+
247+
quoteProto, err := client.GetQuoteProtoAtLevel(qp, reportData, vmpl) // for coverage
248+
if err != nil {
249+
return fmt.Errorf("failed to get quote proto")
250+
}
251+
252+
options := &verify.Options{
253+
CheckRevocations: true,
254+
DisableCertFetching: false,
255+
Getter: trust.DefaultHTTPSGetter(),
256+
Now: time.Now(),
257+
TrustedRoots: nil,
258+
Product: quoteProto.Product,
259+
}
260+
261+
result, err := verify.GetAttestationFromReport(quoteProto.Report, options)
262+
if err != nil {
263+
return fmt.Errorf("could not get fetch certificates: %v", err)
264+
}
265+
266+
homePath, _ := os.UserHomeDir()
267+
268+
vcekPath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), vcekName)
269+
arkAskBundlePath := path.Join(homePath, cocosDirectory, fmt.Sprintf("%d", quoteProto.Product.Name), arkAskBundleName)
270+
271+
vcekPem := derToPem(result.CertificateChain.VcekCert)
272+
askPem := derToPem(result.CertificateChain.AskCert)
273+
arkPem := derToPem(result.CertificateChain.ArkCert)
274+
275+
arkAskBundlePem := append(askPem, arkPem...)
276+
277+
vcekDir := filepath.Dir(vcekPath)
278+
err = os.MkdirAll(vcekDir, 0o755)
279+
if err != nil {
280+
return fmt.Errorf("could not create VCEK directory: %v", err)
281+
}
282+
askArkBundleDir := filepath.Dir(arkAskBundlePath)
283+
err = os.MkdirAll(askArkBundleDir, 0o755)
284+
if err != nil {
285+
return fmt.Errorf("could not create ask/ark bundle directory: %v", err)
286+
}
287+
288+
err = os.WriteFile(vcekPath, vcekPem, 0o644)
289+
if err != nil {
290+
return fmt.Errorf("could not write VCEK file: %v", err)
291+
}
292+
293+
err = os.WriteFile(arkAskBundlePath, arkAskBundlePem, 0o644)
294+
if err != nil {
295+
return fmt.Errorf("could not write ark/ask bundle file: %v", err)
296+
}
297+
298+
return nil
299+
}

pkg/attestation/quoteprovider/sev_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ func TestFillInAttestationLocal(t *testing.T) {
3434
require.NoError(t, err)
3535

3636
bundleContent := []byte("mock ASK ARK bundle")
37-
bundlePath := path.Join(cocosDir, caBundleName)
37+
bundlePath := path.Join(cocosDir, arkAskBundleName)
3838
err = os.WriteFile(bundlePath, bundleContent, 0o644)
3939
require.NoError(t, err)
4040

0 commit comments

Comments
 (0)