blob: 9e040a14ef380aa88c09feebce6e13472e9fcf27 [file] [log] [blame]
package main
import (
"bytes"
"crypto/x509"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/pkg/errors"
"log"
"time"
)
func verify_mra_cert(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error {
printCert(rawCerts[0])
// get the pubkey and payload from raw data
pub_k, payload := unmarshalCert(rawCerts[0])
// Load Intel CA, Verify Cert and Signature
attn_report_raw, err := verifyCert(payload)
if err != nil {
log.Fatalln(err)
return err
}
// Verify attestation report
err = verifyAttReport(attn_report_raw, pub_k)
if err != nil {
log.Fatalln(err)
return err
}
return nil
}
func unmarshalCert(rawbyte []byte) ([]byte, []byte) {
// Search for Public Key prime256v1 OID
prime256v1_oid := []byte{0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07}
offset := uint(bytes.Index(rawbyte, prime256v1_oid))
offset += 11 // 10 + TAG (0x03)
// Obtain Public Key length
length := uint(rawbyte[offset])
if length > 0x80 {
length = uint(rawbyte[offset+1])*uint(0x100) + uint(rawbyte[offset+2])
offset += 2
}
// Obtain Public Key
offset += 1
pub_k := rawbyte[offset+2 : offset+length] // skip "00 04"
// Search for Netscape Comment OID
ns_cmt_oid := []byte{0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x86, 0xF8, 0x42, 0x01, 0x0D}
offset = uint(bytes.Index(rawbyte, ns_cmt_oid))
offset += 12 // 11 + TAG (0x04)
// Obtain Netscape Comment length
length = uint(rawbyte[offset])
if length > 0x80 {
length = uint(rawbyte[offset+1])*uint(0x100) + uint(rawbyte[offset+2])
offset += 2
}
// Obtain Netscape Comment
offset += 1
payload := rawbyte[offset : offset+length]
return pub_k, payload
}
func verifyCert(payload []byte) ([]byte, error) {
// Extract each field
pl_split := bytes.Split(payload, []byte{0x7C})
attn_report_raw := pl_split[0]
sig_raw := pl_split[1]
var sig, sig_cert_dec []byte
sig, err := base64.StdEncoding.DecodeString(string(sig_raw))
if err != nil {
log.Fatalln(err)
return nil, err
}
sig_cert_raw := pl_split[2]
sig_cert_dec, err = base64.StdEncoding.DecodeString(string(sig_cert_raw))
if err != nil {
log.Fatalln(err)
return nil, err
}
certServer, err := x509.ParseCertificate(sig_cert_dec)
if err != nil {
log.Fatalln(err)
return nil, err
}
roots := x509.NewCertPool()
cacert, err := readFile("./../../cert/AttestationReportSigningCACert.pem")
if err != nil {
log.Fatalln(err)
return nil, err
}
ok := roots.AppendCertsFromPEM([]byte(cacert))
if !ok {
panic("failed to parse root certificate")
}
opts := x509.VerifyOptions{
Roots: roots,
}
if _, err := certServer.Verify(opts); err != nil {
log.Fatalln(err)
return nil, err
} else {
fmt.Println("Cert is good")
}
// Verify the signature against the signing cert
err = certServer.CheckSignature(certServer.SignatureAlgorithm, attn_report_raw, sig)
if err != nil {
log.Fatalln(err)
return nil, err
} else {
fmt.Println("Signature good")
}
return attn_report_raw, nil
}
func verifyAttReport(attn_report_raw []byte, pub_k []byte) error {
var qr QuoteReport
err := json.Unmarshal(attn_report_raw, &qr)
if err != nil {
return err
}
// 1. Check timestamp is within 24H
if qr.Timestamp != "" {
//timeFixed := qr.Timestamp + "+0000"
timeFixed := qr.Timestamp + "Z"
ts, _ := time.Parse(time.RFC3339, timeFixed)
now := time.Now().Unix()
fmt.Println("Time diff = ", now-ts.Unix())
} else {
return errors.New("Failed to fetch timestamp from attestation report")
}
// 2. Verify quote status (mandatory field)
if qr.IsvEnclaveQuoteStatus != "" {
fmt.Println("isvEnclaveQuoteStatus = ", qr.IsvEnclaveQuoteStatus)
switch qr.IsvEnclaveQuoteStatus {
case "OK":
break
case "GROUP_OUT_OF_DATE", "GROUP_REVOKED", "CONFIGURATION_NEEDED":
// Verify platformInfoBlob for further info if status not OK
if qr.PlatformInfoBlob != "" {
platInfo, err := hex.DecodeString(qr.PlatformInfoBlob)
if err != nil && len(platInfo) != 105 {
return errors.New("illegal PlatformInfoBlob")
}
platInfo = platInfo[4:]
piBlob := parsePlatform(platInfo)
piBlobJson ,err := json.Marshal(piBlob)
if err != nil{
return err
}
fmt.Println("Platform info is: "+string(piBlobJson))
} else {
return errors.New("Failed to fetch platformInfoBlob from attestation report")
}
default:
return errors.New("SGX_ERROR_UNEXPECTED")
}
} else {
err := errors.New("Failed to fetch isvEnclaveQuoteStatus from attestation report")
return err
}
// 3. Verify quote body
if qr.IsvEnclaveQuoteBody != "" {
qb, err := base64.StdEncoding.DecodeString(qr.IsvEnclaveQuoteBody)
if err != nil {
return err
}
var quoteBytes, quoteHex, pubHex string
for _, b := range qb {
quoteBytes += fmt.Sprint(int(b), ", ")
quoteHex += fmt.Sprintf("%02x", int(b))
}
for _, b := range pub_k {
pubHex += fmt.Sprintf("%02x", int(b))
}
qrData := parseReport(qb, quoteHex)
fmt.Println("Quote = [" + quoteBytes[:len(quoteBytes)-2] + "]")
fmt.Println("sgx quote version = ", qrData.version)
fmt.Println("sgx quote signature type = ", qrData.signType)
fmt.Println("sgx quote report_data = ", qrData.reportBody.reportData)
fmt.Println("sgx quote mr_enclave = ", qrData.reportBody.mrEnclave)
fmt.Println("sgx quote mr_signer = ", qrData.reportBody.mrSigner)
fmt.Println("Anticipated public key = ", pubHex)
if qrData.reportBody.reportData == pubHex {
fmt.Println("ue RA done!")
}
} else {
err := errors.New("Failed to fetch isvEnclaveQuoteBody from attestation report")
return err
}
return nil
}