mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-23 04:10:25 +00:00
6169766666
CI / lint (push) Has been cancelled
CI / test (agent) (push) Has been cancelled
CI / test (cli) (push) Has been cancelled
CI / test (cmd) (push) Has been cancelled
CI / test (internal) (push) Has been cancelled
CI / test (manager, true) (push) Has been cancelled
CI / test (pkg) (push) Has been cancelled
CI / upload-coverage (push) Has been cancelled
* Update attestationFromCert function to include ccPlatform parameter for enhanced attestation processing Signed-off-by: Sammy Oina <sammyoina@gmail.com> * chore: migrate dependencies from supermq to magistrala and update build configurations Signed-off-by: Sammy Oina <sammyoina@gmail.com> * chore: update project dependencies, repository source, and support TDX QuoteV5 attestation Signed-off-by: Sammy Oina <sammyoina@gmail.com> --------- Signed-off-by: Sammy Oina <sammyoina@gmail.com>
299 lines
6.9 KiB
Go
299 lines
6.9 KiB
Go
// Copyright (c) Ultraviolet
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package vtpm
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/absmach/magistrala/pkg/errors"
|
|
"github.com/google/go-sev-guest/proto/sevsnp"
|
|
"github.com/google/go-tpm-tools/client"
|
|
"github.com/google/go-tpm-tools/proto/attest"
|
|
"github.com/google/go-tpm/legacy/tpm2"
|
|
"github.com/google/go-tpm/tpmutil"
|
|
"github.com/ultravioletrs/cocos/pkg/attestation"
|
|
"github.com/veraison/corim/comid"
|
|
"github.com/veraison/corim/corim"
|
|
"golang.org/x/crypto/sha3"
|
|
"google.golang.org/protobuf/proto"
|
|
)
|
|
|
|
var (
|
|
_ attestation.Provider = (*provider)(nil)
|
|
_ attestation.Verifier = (*verifier)(nil)
|
|
)
|
|
|
|
const (
|
|
eventLog = "/sys/kernel/security/tpm0/binary_bios_measurements"
|
|
Nonce = 32
|
|
PCR15 = 15
|
|
PCR16 = 16
|
|
Hash1 = 20
|
|
Hash256 = 32
|
|
Hash384 = 48
|
|
)
|
|
|
|
var (
|
|
ExternalTPM io.ReadWriteCloser
|
|
ErrNoHashAlgo = errors.New("hash algo is not supported")
|
|
ErrFetchQuote = errors.New("failed to fetch vTPM quote")
|
|
)
|
|
|
|
type tpm struct {
|
|
io.ReadWriteCloser
|
|
}
|
|
|
|
func (et tpm) EventLog() ([]byte, error) {
|
|
return os.ReadFile(eventLog)
|
|
}
|
|
|
|
func OpenTpm() (io.ReadWriteCloser, error) {
|
|
if ExternalTPM != nil {
|
|
return tpm{ExternalTPM}, nil
|
|
}
|
|
|
|
tw := tpm{}
|
|
var err error
|
|
|
|
tw.ReadWriteCloser, err = tpm2.OpenTPM("/dev/tpmrm0")
|
|
if os.IsNotExist(err) {
|
|
tw.ReadWriteCloser, err = tpm2.OpenTPM("/dev/tpm0")
|
|
}
|
|
|
|
return tw, err
|
|
}
|
|
|
|
func ExtendPCR(pcrIndex int, value []byte) error {
|
|
rwc, err := OpenTpm()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rwc.Close()
|
|
|
|
fixedSha256Hash := sha3.Sum256(value)
|
|
if err := tpm2.PCRExtend(rwc, tpmutil.Handle(pcrIndex), tpm2.AlgSHA256, fixedSha256Hash[:], ""); err != nil {
|
|
return err
|
|
}
|
|
|
|
fixedSha384Hash := sha3.Sum384(value)
|
|
if err := tpm2.PCRExtend(rwc, tpmutil.Handle(pcrIndex), tpm2.AlgSHA384, fixedSha384Hash[:], ""); err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
type provider struct {
|
|
teeAttestaion bool
|
|
vmpl uint
|
|
}
|
|
|
|
func NewProvider(teeAttestation bool, vmpl uint) attestation.Provider {
|
|
return &provider{
|
|
teeAttestaion: teeAttestation,
|
|
vmpl: vmpl,
|
|
}
|
|
}
|
|
|
|
func (v provider) Attestation(teeNonce []byte, vTpmNonce []byte) ([]byte, error) {
|
|
return Attest(teeNonce, vTpmNonce, v.teeAttestaion, v.vmpl)
|
|
}
|
|
|
|
func (v provider) TeeAttestation(teeNonce []byte) ([]byte, error) {
|
|
return fetchSEVAttestation(teeNonce, v.vmpl)
|
|
}
|
|
|
|
func (a provider) VTpmAttestation(vTpmNonce []byte) ([]byte, error) {
|
|
quote, err := FetchQuote(vTpmNonce)
|
|
if err != nil {
|
|
return []byte{}, errors.Wrap(ErrFetchQuote, err)
|
|
}
|
|
|
|
return proto.Marshal(quote)
|
|
}
|
|
|
|
func (v provider) AzureAttestationToken(tokenNonce []byte) ([]byte, error) {
|
|
return nil, errors.New("Azure attestation token is not supported")
|
|
}
|
|
|
|
type verifier struct {
|
|
writer io.Writer
|
|
}
|
|
|
|
func NewVerifier(writer io.Writer) attestation.Verifier {
|
|
return &verifier{
|
|
writer: writer,
|
|
}
|
|
}
|
|
|
|
func (v *verifier) VerifyWithCoRIM(report []byte, manifest *corim.UnsignedCorim) error {
|
|
attestation := &attest.Attestation{}
|
|
if err := proto.Unmarshal(report, attestation); err != nil {
|
|
return fmt.Errorf("failed to unmarshal attestation report: %w", err)
|
|
}
|
|
|
|
// Extract measurement from SEV-SNP report if present
|
|
snp := attestation.GetSevSnpAttestation()
|
|
if snp == nil {
|
|
return fmt.Errorf("no SEV-SNP attestation found in report")
|
|
}
|
|
|
|
measurement := snp.GetReport().GetMeasurement()
|
|
if len(measurement) == 0 {
|
|
return fmt.Errorf("no measurement in SEV-SNP report")
|
|
}
|
|
|
|
// Iterate over CoMIDs tags looking for measurements
|
|
for _, tag := range manifest.Tags {
|
|
// Expecting a CoMID tag
|
|
if !bytes.HasPrefix(tag, corim.ComidTag) {
|
|
continue
|
|
}
|
|
|
|
tagValue := tag[len(corim.ComidTag):]
|
|
|
|
var c comid.Comid
|
|
if err := c.FromCBOR(tagValue); err != nil {
|
|
return fmt.Errorf("failed to parse CoMID from tag: %w", err)
|
|
}
|
|
|
|
// Match measurements in CoMID
|
|
if c.Triples.ReferenceValues != nil {
|
|
for _, rv := range *c.Triples.ReferenceValues {
|
|
if rv.Measurements.Valid() != nil {
|
|
continue
|
|
}
|
|
for _, m := range rv.Measurements {
|
|
if m.Val.Digests == nil {
|
|
continue
|
|
}
|
|
for _, digest := range *m.Val.Digests {
|
|
if string(digest.HashValue) == string(measurement) {
|
|
return nil // Match found
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// returning nil to satisfy interface for now as we transition
|
|
return nil
|
|
}
|
|
|
|
func Attest(teeNonce []byte, vTPMNonce []byte, teeAttestaion bool, vmpl uint) ([]byte, error) {
|
|
attestation, err := FetchQuote(vTPMNonce)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
if teeAttestaion {
|
|
err = addTEEAttestation(attestation, teeNonce, vmpl)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
}
|
|
|
|
return marshalQuote(attestation)
|
|
}
|
|
|
|
func marshalQuote(attestation *attest.Attestation) ([]byte, error) {
|
|
out, err := proto.Marshal(attestation)
|
|
if err != nil {
|
|
return []byte{}, errors.Wrap(fmt.Errorf("failed to marshal vTPM attestation report"), err)
|
|
}
|
|
|
|
return out, nil
|
|
}
|
|
|
|
func FetchQuote(nonce []byte) (*attest.Attestation, error) {
|
|
rwc, err := OpenTpm()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rwc.Close()
|
|
|
|
attestationKey, err := client.AttestationKeyRSA(rwc)
|
|
if err != nil {
|
|
return nil, errors.Wrap(fmt.Errorf("failed to create attestation key: %v", err), err)
|
|
}
|
|
defer attestationKey.Close()
|
|
|
|
var fixedNonce [Nonce]byte
|
|
copy(fixedNonce[:], nonce)
|
|
attestOpts := client.AttestOpts{}
|
|
attestOpts.Nonce = fixedNonce[:]
|
|
|
|
attestOpts.TCGEventLog, err = client.GetEventLog(rwc)
|
|
if err != nil {
|
|
return nil, errors.Wrap(fmt.Errorf("failed to retrieve TCG Event Log: %v", err), err)
|
|
}
|
|
|
|
attestation, err := attestationKey.Attest(attestOpts)
|
|
if err != nil {
|
|
return nil, errors.Wrap(fmt.Errorf("failed to attest: %v", err), err)
|
|
}
|
|
|
|
return attestation, nil
|
|
}
|
|
|
|
func addTEEAttestation(attestation *attest.Attestation, nonce []byte, vmpl uint) error {
|
|
akPub := attestation.GetAkPub()
|
|
|
|
teeNonce := make([]byte, 0, len(nonce)+len(akPub))
|
|
teeNonce = append(teeNonce, nonce...)
|
|
teeNonce = append(teeNonce, akPub...)
|
|
|
|
attestData := sha3.Sum512(teeNonce)
|
|
|
|
rawTeeAttestation, err := fetchSEVAttestation(attestData[:], vmpl)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to fetch TEE attestation report: %v", err)
|
|
}
|
|
|
|
extReport := &sevsnp.Attestation{}
|
|
err = proto.Unmarshal(rawTeeAttestation, extReport)
|
|
if err != nil {
|
|
return errors.Wrap(fmt.Errorf("failed to unmarshal TEE report proto"), err)
|
|
}
|
|
attestation.TeeAttestation = &attest.Attestation_SevSnpAttestation{
|
|
SevSnpAttestation: extReport,
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func getPCRValue(index int, algorithm tpm2.Algorithm) ([]byte, error) {
|
|
rwc, err := OpenTpm()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rwc.Close()
|
|
|
|
pcrValue, err := tpm2.ReadPCR(rwc, index, algorithm)
|
|
if err != nil {
|
|
if _, ok := ExternalTPM.(*DummyRWC); ok {
|
|
return make([]byte, 20), nil
|
|
}
|
|
return nil, err
|
|
}
|
|
|
|
return pcrValue, nil
|
|
}
|
|
|
|
func GetPCRSHA1Value(index int) ([]byte, error) {
|
|
return getPCRValue(index, tpm2.AlgSHA1)
|
|
}
|
|
|
|
func GetPCRSHA256Value(index int) ([]byte, error) {
|
|
return getPCRValue(index, tpm2.AlgSHA256)
|
|
}
|
|
|
|
func GetPCRSHA384Value(index int) ([]byte, error) {
|
|
return getPCRValue(index, tpm2.AlgSHA384)
|
|
}
|