Skip to content

Commit

Permalink
Merge pull request #5471 from oasisprotocol/ptrus/stable/23.0.x/basck…
Browse files Browse the repository at this point in the history
…ports

[BACKPORT/23.0.x] 5465 5467
  • Loading branch information
ptrus authored Nov 25, 2023
2 parents 992645c + 04d13dd commit c649264
Show file tree
Hide file tree
Showing 18 changed files with 317 additions and 31 deletions.
1 change: 1 addition & 0 deletions .changelog/5465.internal.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/common/sgx: implement `GetPCKCertificateChain` PCS API client
1 change: 1 addition & 0 deletions .changelog/5467.feature.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
go/roothash: expose RoundRoots in the roothash client
1 change: 1 addition & 0 deletions .changelog/5472.bugfix.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
governance: Fix pretty priting of `ChangeParametersProposal`
78 changes: 78 additions & 0 deletions go/common/sgx/pcs/http.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package pcs

import (
"bytes"
"context"
"crypto/x509"
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
Expand All @@ -27,6 +29,7 @@ const (
pcsAPIGetTCBInfoPath = "/certification/v4/tcb"
pcsAPIGetQEIdentityPath = "/certification/v4/qe/identity"
pcsAPICertChainHeader = "TCB-Info-Issuer-Chain"
pcsAPIPCKIIssuerChainHeader = "SGX-PCK-Certificate-Issuer-Chain"
)

// HTTPClientConfig is the Intel SGX PCS client configuration.
Expand Down Expand Up @@ -72,6 +75,7 @@ func (hc *httpClient) doPCSRequest(ctx context.Context, u *url.URL, method, body
"method", method,
"url", u,
)
resp.Body.Close()
return nil, fmt.Errorf("pcs: response status error: %s", http.StatusText(resp.StatusCode))
}

Expand Down Expand Up @@ -133,6 +137,80 @@ func (hc *httpClient) GetTCBBundle(ctx context.Context, fmspc []byte) (*TCBBundl
return &tcbBundle, nil
}

func (hc *httpClient) GetPCKCertificateChain(ctx context.Context, platformData []byte, encPpid [384]byte, cpusvn [16]byte, pcesvn uint16, pceid uint16) ([]*x509.Certificate, error) {
u := hc.getUrl(pcsAPIGetPCKCertificatePath)
q := u.Query()

// Base16-encoded PCESVN value (2 bytes, little endian).
var pcesvnBytes [2]byte
binary.LittleEndian.PutUint16(pcesvnBytes[:], pcesvn)

// Base16-encoded PCE-ID value (2 bytes, little endian)
var pceidBytes [2]byte
binary.LittleEndian.PutUint16(pceidBytes[:], pceid)

var rsp *http.Response
var err error
switch {
case platformData == nil:
// Use GET endpoint with encrypted PPID.
q.Set("encrypted_ppid", hex.EncodeToString(encPpid[:]))
q.Set("cpusvn", hex.EncodeToString(cpusvn[:]))
q.Set("pcesvn", hex.EncodeToString(pcesvnBytes[:]))
q.Set("pceid", hex.EncodeToString(pceidBytes[:]))
u.RawQuery = q.Encode()
rsp, err = hc.doPCSRequest(ctx, u, http.MethodGet, "", nil, false) // nolint: bodyclose
default:
// Platform data is provided, use the POST endpoint with platform data.
payload, merr := json.Marshal(&struct {
PlatformManifest string `json:"platformManifest"`
CPUSVN string `json:"cpusvn"`
PCESVN string `json:"pcesvn"`
PCEID string `json:"pceid"`
}{
PlatformManifest: hex.EncodeToString(platformData),
CPUSVN: hex.EncodeToString(cpusvn[:]),
PCESVN: hex.EncodeToString(pcesvnBytes[:]),
PCEID: hex.EncodeToString(pceidBytes[:]),
})
if merr != nil {
return nil, fmt.Errorf("pcs: failed to marshal PCK certificate request payload: %w", merr)
}
rsp, err = hc.doPCSRequest(ctx, u, http.MethodPost, "application/json", bytes.NewReader(payload), false) // nolint: bodyclose
}
if err != nil {
return nil, fmt.Errorf("pcs: PCK certificate request failed: %w", err)
}
defer rsp.Body.Close()

// Parse issuer Certificate chain for SGX PCK Certificate.
rawCerts, err := url.QueryUnescape(rsp.Header.Get(pcsAPIPCKIIssuerChainHeader))
if err != nil {
return nil, fmt.Errorf("pcs: failed to parse PCK certificate issuer chain header: %w", err)
}
// It consists of SGX Root CA Certificate and SGX Intermediate CA Certificate.
intermediateCert, rest, err := CertFromPEM([]byte(rawCerts))
if err != nil {
return nil, fmt.Errorf("pcs: failed to parse SGX Intermediate CA Certificate from PCK certificate issuer chain: %w", err)
}
rootCert, _, err := CertFromPEM(rest)
if err != nil {
return nil, fmt.Errorf("pcs: failed to parse root SGX Root CA Certificate from PCK certificate issuer chain: %w", err)
}

// Parse PCK Certificate.
rawPCKCert, err := io.ReadAll(rsp.Body)
if err != nil {
return nil, fmt.Errorf("pcs: failed to read PCK certificate response body: %w", err)
}
leafCert, _, err := CertFromPEM(rawPCKCert)
if err != nil {
return nil, fmt.Errorf("pcs: failed to parse PCK certificate: %w", err)
}

return []*x509.Certificate{leafCert, intermediateCert, rootCert}, nil
}

// NewHTTPClient returns a new PCS HTTP endpoint.
func NewHTTPClient(cfg *HTTPClientConfig) (Client, error) {
hc := &httpClient{
Expand Down
6 changes: 6 additions & 0 deletions go/common/sgx/pcs/pcs.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pcs

import (
"context"
"crypto/x509"
"time"

"github.com/oasisprotocol/oasis-core/go/common/sgx"
Expand All @@ -17,6 +18,11 @@ var (
type Client interface {
// GetTCBBundle retrieves the signed TCB artifacts needed to verify a quote.
GetTCBBundle(ctx context.Context, fmspc []byte) (*TCBBundle, error)

// GetPCKCertificateChain retrieves the PCK certificate chain for the given platform data or PPID.
//
// If platform data is provided, it is used instead of the encrypted PPID for certificate retrieval.
GetPCKCertificateChain(ctx context.Context, platformData []byte, encPpid [384]byte, cpusvn [16]byte, pcesvn uint16, pceid uint16) ([]*x509.Certificate, error)
}

// QuoteBundle is an attestation quote together with the TCB bundle required for its verification.
Expand Down
13 changes: 10 additions & 3 deletions go/common/sgx/pcs/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"encoding/binary"
"encoding/hex"
"fmt"
"math"
"math/big"
"time"

Expand Down Expand Up @@ -363,7 +364,7 @@ type PCKInfo struct {
PublicKey *ecdsa.PublicKey
FMSPC []byte
TCBCompSVN [16]int32
PCESVN int32
PCESVN uint16
CPUSVN [16]byte
}

Expand Down Expand Up @@ -420,15 +421,21 @@ func (qs *QuoteSignatureECDSA_P256) VerifyPCK(ts time.Time) (*PCKInfo, error) {
}
case compId == 17:
// PCESVN
if _, err = asn1.Unmarshal(tcbExt.Value.FullBytes, &pckInfo.PCESVN); err != nil {
var pcesvn int32
if _, err = asn1.Unmarshal(tcbExt.Value.FullBytes, &pcesvn); err != nil {
return nil, fmt.Errorf("pcs/quote: bad PCESVN: %w", err)
}
if pcesvn < 0 || pcesvn > math.MaxUint16 {
return nil, fmt.Errorf("pcs/quote: bad PCESVN value: %d (not uint16)", pcesvn)
}
pckInfo.PCESVN = uint16(pcesvn)
case compId == 18:
// CPUSVN
cpusvnSlice := pckInfo.CPUSVN[:]
var cpusvnSlice []byte
if _, err = asn1.Unmarshal(tcbExt.Value.FullBytes, &cpusvnSlice); err != nil {
return nil, fmt.Errorf("pcs/quote: bad CPUSVN: %w", err)
}
copy(pckInfo.CPUSVN[:], cpusvnSlice)
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions go/common/sgx/pcs/tcb.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ func (bnd *TCBBundle) Verify(
policy *QuotePolicy,
fmspc []byte,
tcbCompSvn [16]int32,
pcesvn int32,
pcesvn uint16,
qe *ReportBody,
) error {
pk, err := bnd.getPublicKey(ts)
Expand Down Expand Up @@ -84,7 +84,7 @@ func (bnd *TCBBundle) verifyTCBInfo(
policy *QuotePolicy,
fmspc []byte,
tcbCompSvn [16]int32,
pcesvn int32,
pcesvn uint16,
) error {
tcbInfo, err := bnd.TCBInfo.open(ts, policy, pk)
if err != nil {
Expand Down Expand Up @@ -261,7 +261,7 @@ func (ti *TCBInfo) validateFMSPC(fmspc []byte) error {

func (ti *TCBInfo) validateTCBLevel(
tcbCompSvn [16]int32,
pcesvn int32,
pcesvn uint16,
) error {
tcbLevel, err := ti.getTCBLevel(tcbCompSvn, pcesvn)
if err != nil {
Expand All @@ -284,7 +284,7 @@ func (ti *TCBInfo) validateTCBLevel(

func (ti *TCBInfo) getTCBLevel(
tcbCompSvn [16]int32,
pcesvn int32,
pcesvn uint16,
) (*TCBLevel, error) {
// Find first matching TCB level.
var matchedTCBLevel *TCBLevel
Expand Down Expand Up @@ -350,7 +350,7 @@ type TCBComponent struct {
// TCBLevel is a platform TCB level.
type TCBLevel struct {
TCB struct {
PCESVN int32 `json:"pcesvn"`
PCESVN uint16 `json:"pcesvn"`
SGXComponents [16]TCBComponent `json:"sgxtcbcomponents"`
TDXComponents [16]TCBComponent `json:"tdxtcbcomponents,omitempty"`
} `json:"tcb"`
Expand All @@ -360,7 +360,7 @@ type TCBLevel struct {
}

// matches performs the SVN comparison.
func (tl *TCBLevel) matches(tcbCompSvn [16]int32, pcesvn int32) bool {
func (tl *TCBLevel) matches(tcbCompSvn [16]int32, pcesvn uint16) bool {
// a) Compare all of the SGX TCB Comp SVNs retrieved from the SGX PCK Certificate (from 01 to
// 16) with the corresponding values in the TCB Level. If all SGX TCB Comp SVNs in the
// certificate are greater or equal to the corresponding values in TCB Level, go to b,
Expand Down
10 changes: 10 additions & 0 deletions go/consensus/cometbft/apps/roothash/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ type Query interface {
GenesisBlock(context.Context, common.Namespace) (*block.Block, error)
RuntimeState(context.Context, common.Namespace) (*roothash.RuntimeState, error)
LastRoundResults(context.Context, common.Namespace) (*roothash.RoundResults, error)
RoundRoots(context.Context, common.Namespace, uint64) (*roothash.RoundRoots, error)
PastRoundRoots(context.Context, common.Namespace) (map[uint64]roothash.RoundRoots, error)
IncomingMessageQueueMeta(context.Context, common.Namespace) (*message.IncomingMessageQueueMeta, error)
IncomingMessageQueue(ctx context.Context, id common.Namespace, offset uint64, limit uint32) ([]*message.IncomingMessage, error)
Genesis(context.Context) (*roothash.Genesis, error)
Expand Down Expand Up @@ -65,6 +67,14 @@ func (rq *rootHashQuerier) LastRoundResults(ctx context.Context, id common.Names
return rq.state.LastRoundResults(ctx, id)
}

func (rq *rootHashQuerier) RoundRoots(ctx context.Context, id common.Namespace, round uint64) (*roothash.RoundRoots, error) {
return rq.state.RoundRoots(ctx, id, round)
}

func (rq *rootHashQuerier) PastRoundRoots(ctx context.Context, id common.Namespace) (map[uint64]roothash.RoundRoots, error) {
return rq.state.PastRoundRoots(ctx, id)
}

func (rq *rootHashQuerier) IncomingMessageQueueMeta(ctx context.Context, id common.Namespace) (*message.IncomingMessageQueueMeta, error) {
return rq.state.IncomingMessageQueueMeta(ctx, id)
}
Expand Down
18 changes: 18 additions & 0 deletions go/consensus/cometbft/roothash/roothash.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,24 @@ func (sc *serviceClient) GetLastRoundResults(ctx context.Context, request *api.R
return q.LastRoundResults(ctx, request.RuntimeID)
}

func (sc *serviceClient) GetRoundRoots(ctx context.Context, request *api.RoundRootsRequest) (*api.RoundRoots, error) {
q, err := sc.querier.QueryAt(ctx, request.Height)
if err != nil {
return nil, err
}

return q.RoundRoots(ctx, request.RuntimeID, request.Round)
}

func (sc *serviceClient) GetPastRoundRoots(ctx context.Context, request *api.RuntimeRequest) (map[uint64]api.RoundRoots, error) {
q, err := sc.querier.QueryAt(ctx, request.Height)
if err != nil {
return nil, err
}

return q.PastRoundRoots(ctx, request.RuntimeID)
}

// Implements api.Backend.
func (sc *serviceClient) GetIncomingMessageQueueMeta(ctx context.Context, request *api.RuntimeRequest) (*message.IncomingMessageQueueMeta, error) {
q, err := sc.querier.QueryAt(ctx, request.Height)
Expand Down
29 changes: 19 additions & 10 deletions go/governance/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package api
import (
"bytes"
"context"
"encoding/base64"
"fmt"
"io"
"reflect"
Expand All @@ -23,10 +24,6 @@ import (
// ModuleName is a unique module name for the governance backend.
const ModuleName = "governance"

// ProposalContentInvalidText is the textual representation of an invalid
// ProposalContent.
const ProposalContentInvalidText = "(invalid)"

var (
// ErrInvalidArgument is the error returned on malformed argument(s).
ErrInvalidArgument = errors.New(ModuleName, 1, "governance: invalid argument")
Expand Down Expand Up @@ -124,15 +121,17 @@ func (p *ProposalContent) Equals(other *ProposalContent) bool {
// PrettyPrint writes a pretty-printed representation of ProposalContent to the
// given writer.
func (p ProposalContent) PrettyPrint(ctx context.Context, prefix string, w io.Writer) {
switch {
case p.Upgrade != nil && p.CancelUpgrade == nil:
if p.Upgrade != nil {
fmt.Fprintf(w, "%sUpgrade:\n", prefix)
p.Upgrade.PrettyPrint(ctx, prefix+" ", w)
case p.CancelUpgrade != nil && p.Upgrade == nil:
}
if p.CancelUpgrade != nil {
fmt.Fprintf(w, "%sCancel Upgrade:\n", prefix)
p.CancelUpgrade.PrettyPrint(ctx, prefix+" ", w)
default:
fmt.Fprintf(w, "%s%s\n", prefix, ProposalContentInvalidText)
}
if p.ChangeParameters != nil {
fmt.Fprintf(w, "%sChange Parameters:\n", prefix)
p.ChangeParameters.PrettyPrint(ctx, prefix+" ", w)
}
}

Expand Down Expand Up @@ -233,8 +232,18 @@ func (p *ChangeParametersProposal) Equals(other *ChangeParametersProposal) bool
// PrettyPrint writes a pretty-printed representation of ChangeParametersProposal to the given
// writer.
func (p *ChangeParametersProposal) PrettyPrint(_ context.Context, prefix string, w io.Writer) {
var changes map[string]interface{}
if err := cbor.Unmarshal(p.Changes, &changes); err != nil {
fmt.Fprintf(w, "%s <error: %s>\n", prefix, err)
fmt.Fprintf(w, "%s <malformed: %s>\n", prefix, base64.StdEncoding.EncodeToString(p.Changes))
return
}
fmt.Fprintf(w, "%sModule: %s\n", prefix, p.Module)
fmt.Fprintf(w, "%sChanges: %v\n", prefix, p.Changes)
fmt.Fprintf(w, "%sChanges: \n", prefix)
for param, value := range changes {
fmt.Fprintf(w, "%s - Parameter: %s\n", prefix, param)
fmt.Fprintf(w, "%s Value: %v\n", prefix, value)
}
}

// PrettyType returns a representation of ChangeParametersProposal that can be used for pretty
Expand Down
12 changes: 7 additions & 5 deletions go/governance/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,16 +163,18 @@ func TestProposalContentPrettyPrint(t *testing.T) {
},
},
{
expRegex: ProposalContentInvalidText,
expRegex: "",
p: &ProposalContent{},
},
{
expRegex: ProposalContentInvalidText,
expRegex: "^Change Parameters:",
p: &ProposalContent{
Upgrade: &UpgradeProposal{
Descriptor: upgrade.Descriptor{Handler: "test"},
ChangeParameters: &ChangeParametersProposal{
Module: "test-module",
Changes: cbor.Marshal(map[string]string{
"test-parameter": "test-value",
}),
},
CancelUpgrade: &CancelUpgradeProposal{ProposalID: 42},
},
},
} {
Expand Down
8 changes: 2 additions & 6 deletions go/ias/http/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ func (e *httpEndpoint) doIASRequest(ctx context.Context, method, uPath, bodyType
}
if resp.StatusCode != http.StatusOK {
logger.Error("ias response status error", "status", http.StatusText(resp.StatusCode), "method", method, "url", u)
resp.Body.Close()
return nil, fmt.Errorf("ias: response status error: %s", http.StatusText(resp.StatusCode))
}

Expand Down Expand Up @@ -126,9 +127,6 @@ func (e *httpEndpoint) VerifyEvidence(ctx context.Context, evidence *api.Evidenc
query = []string{iasAPIAVRTCBUpdateParam, iasAPIAVRTCBUpdateValueEarly}
}
resp, err := e.doIASRequest(ctx, http.MethodPost, iasAPIAttestationReportPath, "application/json", bytes.NewReader(reqPayload), query...)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, fmt.Errorf("ias: http POST failed: %w", err)
}
Expand Down Expand Up @@ -165,12 +163,10 @@ func (e *httpEndpoint) GetSigRL(ctx context.Context, epidGID uint32) ([]byte, er
// Dispatch the request via HTTP.
p := path.Join(iasAPISigRLPath, hex.EncodeToString(gid[:]))
resp, err := e.doIASRequest(ctx, http.MethodGet, p, "", nil)
if resp != nil {
defer resp.Body.Close()
}
if err != nil {
return nil, fmt.Errorf("ias: http GET failed: %w", err)
}
defer resp.Body.Close()

// Extract and parse the SigRL.
sigRL, err := io.ReadAll(resp.Body)
Expand Down
Loading

0 comments on commit c649264

Please sign in to comment.