77package quoteprovider
88
99import (
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
2732const (
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
177210func 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+ }
0 commit comments