mirror of
https://github.com/ultravioletrs/cocos.git
synced 2026-06-22 20:00:18 +00:00
NOISSUE - Enforce binding label check (#589)
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
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
This commit is contained in:
committed by
GitHub
parent
b44780df95
commit
3b9841a973
@@ -347,19 +347,22 @@ func validateAuthenticator(session *Session, st *tls.ConnectionState, role Role,
|
||||
Context: append([]byte(nil), certMsg.Context...),
|
||||
Chain: chain,
|
||||
}
|
||||
var verifierPolicy eaattestation.VerificationPolicy
|
||||
// A nil attestation policy is intentional: VerifyPayload then fails closed
|
||||
// for any payload that carries evidence or attestation results without
|
||||
// explicit verifiers being configured.
|
||||
if attPolicy != nil {
|
||||
verifierPolicy = *attPolicy
|
||||
}
|
||||
if !present && verifierPolicy.RequiresAttestation() {
|
||||
return nil, eaattestation.ErrMissingAttestation
|
||||
}
|
||||
if present {
|
||||
res.CMWAttestation = extracted
|
||||
parsed, err := eaattestation.ParsePayload(extracted)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var verifierPolicy eaattestation.VerificationPolicy
|
||||
// A nil attestation policy is intentional: VerifyPayload then fails closed
|
||||
// for any payload that carries evidence or attestation results without
|
||||
// explicit verifiers being configured.
|
||||
if attPolicy != nil {
|
||||
verifierPolicy = *attPolicy
|
||||
}
|
||||
verified, err := eaattestation.VerifyPayload(st, eaattestation.ExporterLabelAttestation, certMsg.Context, leaf, parsed, verifierPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -64,6 +64,8 @@ type acceptEvidenceVerifier struct{}
|
||||
|
||||
func (acceptEvidenceVerifier) VerifyEvidence(evidence []byte) error { return nil }
|
||||
|
||||
const alternateExporterLabel = "Attestation Binding"
|
||||
|
||||
func TestDummyAttestationRoundTrip(t *testing.T) {
|
||||
cert := selfSignedCert(t)
|
||||
srv, cli := tlsPair(t, cert)
|
||||
@@ -127,6 +129,63 @@ func TestDummyAttestationRoundTrip(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestDummyAttestationRoundTripRejectsAlternateExporterLabel(t *testing.T) {
|
||||
cert := selfSignedCert(t)
|
||||
srv, cli := tlsPair(t, cert)
|
||||
defer srv.Close()
|
||||
defer cli.Close()
|
||||
|
||||
ctx, _ := NewRandomContext(16)
|
||||
req := &AuthenticatorRequest{
|
||||
Type: HandshakeTypeClientCertificateRequest,
|
||||
Context: ctx,
|
||||
Extensions: []Extension{
|
||||
{Type: SignatureAlgorithmsExtensionType, Data: []byte{0x00, 0x02, 0x04, 0x03}},
|
||||
CMWAttestationOfferExtension(),
|
||||
},
|
||||
}
|
||||
|
||||
leaf, _ := x509.ParseCertificate(cert.Certificate[0])
|
||||
srvState := srv.ConnectionState()
|
||||
_, aikPubHash, binding, err := attestation.ComputeBinding(&srvState, alternateExporterLabel, ctx, leaf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
payloadBytes, err := attestation.MarshalPayload(attestation.Payload{
|
||||
Version: 1,
|
||||
Evidence: []byte("dummy-attestation-report"),
|
||||
MediaType: "application/eat+cwt",
|
||||
Binder: attestation.AttestationBinder{
|
||||
ExporterLabel: alternateExporterLabel,
|
||||
AIKPubHash: aikPubHash,
|
||||
Binding: binding,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
ext, err := CMWAttestationDataExtension(payloadBytes)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
auth, err := CreateAuthenticator(&srvState, RoleServer, req, cert, []Extension{ext})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cliState := cli.ConnectionState()
|
||||
roots := x509.NewCertPool()
|
||||
roots.AddCert(leaf)
|
||||
|
||||
_, err = ValidateAuthenticatorWithAttestation(&cliState, RoleServer, req, auth, &x509.VerifyOptions{Roots: roots}, attestation.VerificationPolicy{
|
||||
EvidenceVerifier: acceptEvidenceVerifier{},
|
||||
})
|
||||
if err != attestation.ErrUnexpectedExporterLabel {
|
||||
t.Fatalf("got %v, want %v", err, attestation.ErrUnexpectedExporterLabel)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRejectIfNotOffered(t *testing.T) {
|
||||
cert := selfSignedCert(t)
|
||||
srv, cli := tlsPair(t, cert)
|
||||
@@ -199,6 +258,44 @@ func TestAttestationFailsClosedWithoutVerifier(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAuthenticatorRejectsMissingOfferedAttestation(t *testing.T) {
|
||||
cert := selfSignedCert(t)
|
||||
srv, cli := tlsPair(t, cert)
|
||||
defer srv.Close()
|
||||
defer cli.Close()
|
||||
|
||||
ctx, _ := NewRandomContext(16)
|
||||
req := &AuthenticatorRequest{
|
||||
Type: HandshakeTypeClientCertificateRequest,
|
||||
Context: ctx,
|
||||
Extensions: []Extension{
|
||||
{Type: SignatureAlgorithmsExtensionType, Data: []byte{0x00, 0x02, 0x04, 0x03}},
|
||||
CMWAttestationOfferExtension(),
|
||||
},
|
||||
}
|
||||
|
||||
srvState := srv.ConnectionState()
|
||||
auth, err := CreateAuthenticator(&srvState, RoleServer, req, cert, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cliState := cli.ConnectionState()
|
||||
roots := x509.NewCertPool()
|
||||
leaf, err := x509.ParseCertificate(cert.Certificate[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
roots.AddCert(leaf)
|
||||
|
||||
_, err = ValidateAuthenticatorWithAttestation(&cliState, RoleServer, req, auth, &x509.VerifyOptions{Roots: roots}, attestation.VerificationPolicy{
|
||||
EvidenceVerifier: acceptEvidenceVerifier{},
|
||||
})
|
||||
if err != attestation.ErrMissingAttestation {
|
||||
t.Fatalf("got %v, want %v", err, attestation.ErrMissingAttestation)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRejectCMWAttestationOnIntermediateEntry(t *testing.T) {
|
||||
cert := selfSignedCert(t)
|
||||
srv, cli := tlsPair(t, cert)
|
||||
|
||||
@@ -3,19 +3,22 @@
|
||||
|
||||
package ea
|
||||
|
||||
const CMWAttestationExtensionType uint16 = 0xFF00
|
||||
const (
|
||||
CMWAttestationExtensionType uint16 = 0xFF00
|
||||
cmwAttestationLengthBytes = 2
|
||||
)
|
||||
|
||||
func CMWAttestationOfferExtension() Extension {
|
||||
return Extension{Type: CMWAttestationExtensionType, Data: nil}
|
||||
}
|
||||
|
||||
func CMWAttestationDataExtension(cmw []byte) (Extension, error) {
|
||||
if len(cmw) == 0 || len(cmw) > 0xFFFF {
|
||||
if len(cmw) == 0 || len(cmw)+cmwAttestationLengthBytes > 0xFFFF {
|
||||
return Extension{}, ErrInvalidLength
|
||||
}
|
||||
data := make([]byte, 2+len(cmw))
|
||||
data := make([]byte, cmwAttestationLengthBytes+len(cmw))
|
||||
putUint16(data[0:2], uint16(len(cmw)))
|
||||
copy(data[2:], cmw)
|
||||
copy(data[cmwAttestationLengthBytes:], cmw)
|
||||
return Extension{Type: CMWAttestationExtensionType, Data: data}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -12,12 +12,10 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ExporterLabelAttestation = "Attestation"
|
||||
ExporterLabelAttestationBinding = "Attestation Binding"
|
||||
ExporterLabelAttestation = "Attestation"
|
||||
ExportedAttestationValueLen = 32
|
||||
)
|
||||
|
||||
const ExportedAttestationValueLen = 32
|
||||
|
||||
var errNotTLS13 = errors.New("attestation: not TLS 1.3")
|
||||
|
||||
func ExportAttestationValue(st *tls.ConnectionState, label string, contextValue []byte) ([]byte, crypto.Hash, error) {
|
||||
|
||||
@@ -17,6 +17,8 @@ import (
|
||||
"time"
|
||||
)
|
||||
|
||||
const alternateExporterLabel = "Attestation Binding"
|
||||
|
||||
type stubEvidenceVerifier struct {
|
||||
called bool
|
||||
err error
|
||||
@@ -175,6 +177,37 @@ func TestVerifyPayloadSuccess(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyPayloadRejectsAlternateExporterLabel(t *testing.T) {
|
||||
cert, leaf := makeCert(t)
|
||||
srv, cli := tls13Client(t, cert)
|
||||
defer srv.Close()
|
||||
defer cli.Close()
|
||||
|
||||
st := cli.ConnectionState()
|
||||
ctx := []byte{1, 2, 3, 4}
|
||||
_, aik, binding, err := ComputeBinding(&st, alternateExporterLabel, ctx, leaf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
payload := &Payload{
|
||||
Version: 1,
|
||||
Evidence: []byte("evidence"),
|
||||
Binder: AttestationBinder{
|
||||
ExporterLabel: alternateExporterLabel,
|
||||
AIKPubHash: aik,
|
||||
Binding: binding,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = VerifyPayload(&st, ExporterLabelAttestation, ctx, leaf, payload, VerificationPolicy{
|
||||
EvidenceVerifier: &stubEvidenceVerifier{},
|
||||
})
|
||||
if err != ErrUnexpectedExporterLabel {
|
||||
t.Fatalf("got %v, want %v", err, ErrUnexpectedExporterLabel)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyBinderRejectsMismatch(t *testing.T) {
|
||||
cert, leaf := makeCert(t)
|
||||
srv, cli := tls13Client(t, cert)
|
||||
@@ -197,3 +230,39 @@ func TestVerifyBinderRejectsMismatch(t *testing.T) {
|
||||
t.Fatalf("got %v, want %v", err, ErrBindingMismatch)
|
||||
}
|
||||
}
|
||||
|
||||
func TestVerifyPayloadRejectsBadBinderBeforeEvidenceVerification(t *testing.T) {
|
||||
cert, leaf := makeCert(t)
|
||||
srv, cli := tls13Client(t, cert)
|
||||
defer srv.Close()
|
||||
defer cli.Close()
|
||||
|
||||
st := cli.ConnectionState()
|
||||
ctx := []byte{1, 2, 3, 4}
|
||||
_, aik, binding, err := ComputeBinding(&st, ExporterLabelAttestation, ctx, leaf)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
binding[0] ^= 0xff
|
||||
|
||||
ev := &stubEvidenceVerifier{}
|
||||
payload := &Payload{
|
||||
Version: 1,
|
||||
Evidence: []byte("evidence"),
|
||||
Binder: AttestationBinder{
|
||||
ExporterLabel: ExporterLabelAttestation,
|
||||
AIKPubHash: aik,
|
||||
Binding: binding,
|
||||
},
|
||||
}
|
||||
|
||||
_, err = VerifyPayload(&st, ExporterLabelAttestation, ctx, leaf, payload, VerificationPolicy{
|
||||
EvidenceVerifier: ev,
|
||||
})
|
||||
if err != ErrBindingMismatch {
|
||||
t.Fatalf("got %v, want %v", err, ErrBindingMismatch)
|
||||
}
|
||||
if ev.called {
|
||||
t.Fatalf("evidence verifier should not be called before binder verification succeeds")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ var (
|
||||
ErrMissingBinder = errors.New("attestation: missing attestation binder")
|
||||
ErrAIKPubHashMismatch = errors.New("attestation: AIK public key hash mismatch")
|
||||
ErrBindingMismatch = errors.New("attestation: attestation binding mismatch")
|
||||
ErrUnexpectedExporterLabel = errors.New("attestation: unexpected exporter label")
|
||||
ErrMissingAttestation = errors.New("attestation: missing attestation payload")
|
||||
ErrEvidenceVerificationMissing = errors.New("attestation: evidence verifier not configured")
|
||||
ErrResultsVerificationMissing = errors.New("attestation: attestation results verifier not configured")
|
||||
)
|
||||
@@ -62,6 +64,16 @@ func (p *Payload) NormalizedExporterLabel(defaultLabel string) string {
|
||||
return p.Binder.ExporterLabel
|
||||
}
|
||||
|
||||
func (p *Payload) VerifyExporterLabel(expectedLabel string) error {
|
||||
if p == nil {
|
||||
return ErrMalformedPayload
|
||||
}
|
||||
if p.Binder.ExporterLabel != "" && p.Binder.ExporterLabel != expectedLabel {
|
||||
return ErrUnexpectedExporterLabel
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func MarshalPayload(p Payload) ([]byte, error) {
|
||||
if err := p.Validate(); err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -22,16 +22,28 @@ type VerificationPolicy struct {
|
||||
ResultsVerifier ResultsVerifier
|
||||
}
|
||||
|
||||
func (p VerificationPolicy) RequiresAttestation() bool {
|
||||
return p.EvidenceVerifier != nil || p.ResultsVerifier != nil
|
||||
}
|
||||
|
||||
func VerifyPayload(st *tls.ConnectionState, defaultLabel string, certificateRequestContext []byte, leaf *x509.Certificate, payload *Payload, policy VerificationPolicy) (*VerifiedPayload, error) {
|
||||
if err := payload.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := payload.VerifyExporterLabel(defaultLabel); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
verified := &VerifiedPayload{
|
||||
Payload: payload,
|
||||
UsedExporterLabel: payload.NormalizedExporterLabel(defaultLabel),
|
||||
UsedExporterLabel: defaultLabel,
|
||||
}
|
||||
|
||||
if err := VerifyBinder(st, verified.UsedExporterLabel, certificateRequestContext, leaf, payload.Binder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verified.BindingVerified = true
|
||||
|
||||
if len(payload.Evidence) > 0 && policy.EvidenceVerifier != nil {
|
||||
if err := policy.EvidenceVerifier.VerifyEvidence(payload.Evidence); err != nil {
|
||||
return nil, err
|
||||
@@ -48,10 +60,6 @@ func VerifyPayload(st *tls.ConnectionState, defaultLabel string, certificateRequ
|
||||
} else if len(payload.AttestationResults) > 0 {
|
||||
return nil, ErrResultsVerificationMissing
|
||||
}
|
||||
if err := VerifyBinder(st, verified.UsedExporterLabel, certificateRequestContext, leaf, payload.Binder); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
verified.BindingVerified = true
|
||||
return verified, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -168,6 +168,9 @@ func Server(tlsConn *tls.Conn, cfg *ServerConfig) (*Conn, error) {
|
||||
if len(rest) != 0 {
|
||||
return nil, fmt.Errorf("atls: trailing request bytes")
|
||||
}
|
||||
if cfg.BuildLeafExtensions != nil && !ea.RequestPermitsCertificateExtension(&req, ea.CMWAttestationExtensionType) {
|
||||
return nil, fmt.Errorf("atls: cmw_attestation extension not offered")
|
||||
}
|
||||
|
||||
st := tlsConn.ConnectionState()
|
||||
identity, err := resolveIdentity(cfg)
|
||||
|
||||
Reference in New Issue
Block a user