Skip to content

Commit 865bfd0

Browse files
authored
did:x509: improved ca-fingerprint validation (#3607)
1 parent 3d62c67 commit 865bfd0

File tree

10 files changed

+181
-104
lines changed

10 files changed

+181
-104
lines changed

network/network_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1257,7 +1257,7 @@ func TestNetwork_checkHealth(t *testing.T) {
12571257
t.Run("TLS", func(t *testing.T) {
12581258
t.Run("up", func(t *testing.T) {
12591259
mockPKIValidator := pki.NewMockValidator(gomock.NewController(t))
1260-
mockPKIValidator.EXPECT().Validate([]*x509.Certificate{certificate.Leaf})
1260+
mockPKIValidator.EXPECT().CheckCRL([]*x509.Certificate{certificate.Leaf})
12611261
n := Network{
12621262
trustStore: trustStore,
12631263
certificate: certificate,
@@ -1270,7 +1270,7 @@ func TestNetwork_checkHealth(t *testing.T) {
12701270
})
12711271
t.Run("revoked/denied certificate", func(t *testing.T) {
12721272
mockPKIValidator := pki.NewMockValidator(gomock.NewController(t))
1273-
mockPKIValidator.EXPECT().Validate([]*x509.Certificate{certificate.Leaf}).Return(errors.New("custom error"))
1273+
mockPKIValidator.EXPECT().CheckCRL([]*x509.Certificate{certificate.Leaf}).Return(errors.New("custom error"))
12741274
n := Network{
12751275
trustStore: trustStore,
12761276
certificate: certificate,
@@ -1322,7 +1322,7 @@ func TestNetwork_checkHealth(t *testing.T) {
13221322
cxt.network.certificate = certificate
13231323
cxt.network.nodeDID = *nodeDID
13241324
cxt.didStore.EXPECT().Resolve(*nodeDID, nil).MinTimes(1).Return(completeDocument, &resolver.DocumentMetadata{}, nil)
1325-
cxt.pkiValidator.EXPECT().Validate([]*x509.Certificate{certificate.Leaf})
1325+
cxt.pkiValidator.EXPECT().CheckCRL([]*x509.Certificate{certificate.Leaf})
13261326

13271327
health := cxt.network.CheckHealth()
13281328

network/transport/grpc/connection_manager_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,7 @@ func Test_grpcConnectionManager_revalidatePeers(t *testing.T) {
12231223
cert := testPKI.Certificate().Leaf
12241224

12251225
t.Run("ok", func(t *testing.T) {
1226-
mockValidator.EXPECT().Validate([]*x509.Certificate{cert})
1226+
mockValidator.EXPECT().CheckCRL([]*x509.Certificate{cert})
12271227
cm, err := NewGRPCConnectionManager(Config{pkiValidator: mockValidator}, nil, *nodeDID, nil)
12281228
require.NoError(t, err)
12291229
connection := NewStubConnection(transport.Peer{Certificate: cert})
@@ -1234,7 +1234,7 @@ func Test_grpcConnectionManager_revalidatePeers(t *testing.T) {
12341234
assert.Equal(t, 0, connection.disconnectCalls)
12351235
})
12361236
t.Run("denied", func(t *testing.T) {
1237-
mockValidator.EXPECT().Validate([]*x509.Certificate{cert}).Return(pki.ErrCertBanned)
1237+
mockValidator.EXPECT().CheckCRL([]*x509.Certificate{cert}).Return(pki.ErrCertBanned)
12381238
cm, err := NewGRPCConnectionManager(Config{pkiValidator: mockValidator}, nil, *nodeDID, nil)
12391239
require.NoError(t, err)
12401240
connection := NewStubConnection(transport.Peer{Certificate: cert})
@@ -1245,7 +1245,7 @@ func Test_grpcConnectionManager_revalidatePeers(t *testing.T) {
12451245
assert.Equal(t, 1, connection.disconnectCalls)
12461246
})
12471247
t.Run("denied multiple", func(t *testing.T) {
1248-
mockValidator.EXPECT().Validate([]*x509.Certificate{cert}).Return(pki.ErrCertBanned).Times(3)
1248+
mockValidator.EXPECT().CheckCRL([]*x509.Certificate{cert}).Return(pki.ErrCertBanned).Times(3)
12491249
cm, err := NewGRPCConnectionManager(Config{pkiValidator: mockValidator}, nil, *nodeDID, nil)
12501250
require.NoError(t, err)
12511251
connection := NewStubConnection(transport.Peer{Certificate: cert})

network/transport/grpc/tls_offloading_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ func Test_tlsOffloadingAuthenticator(t *testing.T) {
7474
var peerInfo *peer.Peer
7575
var success bool
7676
serverStream.ctx = contextWithMD(encodedCert)
77-
pkiMock.EXPECT().Validate(gomock.Any())
77+
pkiMock.EXPECT().CheckCRL(gomock.Any())
7878

7979
err := auth.intercept(nil, serverStream, nil, func(srv interface{}, wrappedStream grpc.ServerStream) error {
8080
peerInfo, success = peer.FromContext(wrappedStream.Context())
@@ -99,7 +99,7 @@ func Test_tlsOffloadingAuthenticator(t *testing.T) {
9999
})
100100
t.Run("certificate revoked/banned", func(t *testing.T) {
101101
serverStream.ctx = contextWithMD(encodedCert)
102-
pkiMock.EXPECT().Validate(gomock.Any()).Return(errors.New("custom error"))
102+
pkiMock.EXPECT().CheckCRL(gomock.Any()).Return(errors.New("custom error"))
103103

104104
err := auth.intercept(nil, serverStream, nil, func(srv interface{}, wrappedStream grpc.ServerStream) error {
105105
t.Fatal("should not be called")

pki/mock.go

Lines changed: 54 additions & 54 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

storage/engine_azuresql_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
/*
2+
* Copyright (C) 2024 Nuts community
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*
17+
*/
18+
119
package storage
220

321
import (

storage/mock.go

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

storage/orm/did_document_test.go

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,21 @@
1+
/*
2+
* Copyright (C) 2024 Nuts community
3+
*
4+
* This program is free software: you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation, either version 3 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
16+
*
17+
*/
18+
119
package orm
220

321
import (

vcr/verifier/signature_verifier_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ func TestSignatureVerifier_VerifySignature(t *testing.T) {
100100

101101
t.Run("happy flow", func(t *testing.T) {
102102
sv, validator := x509VerifierTestSetup(t)
103-
validator.EXPECT().ValidateStrict(gomock.Any()).Return(nil)
103+
validator.EXPECT().CheckCRLStrict(gomock.Any()).Return(nil)
104104
err = sv.VerifySignature(*cred, nil)
105105
assert.NoError(t, err)
106106
})
@@ -121,7 +121,7 @@ func TestSignatureVerifier_VerifySignature(t *testing.T) {
121121
assert.NoError(t, err)
122122
sv, validator := x509VerifierTestSetup(t)
123123
expectedError := errors.New("wrong ura")
124-
validator.EXPECT().ValidateStrict(gomock.Any()).Return(expectedError)
124+
validator.EXPECT().CheckCRLStrict(gomock.Any()).Return(expectedError)
125125
err = sv.VerifySignature(*cred, nil)
126126
assert.Error(t, err)
127127
assert.ErrorIs(t, err, expectedError)

vdr/didx509/resolver.go

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
package didx509
2020

2121
import (
22+
"bytes"
2223
"crypto/x509"
2324
"errors"
2425
"fmt"
@@ -47,8 +48,8 @@ const (
4748

4849
var (
4950

50-
// ErrX509ChainMissing is returned when the x509 root certificate chain is not present in the metadata.
51-
ErrX509ChainMissing = errors.New("x509 rootCert chain is missing")
51+
// ErrX509ChainMissing indicates that no x5c header was found in the provided metadata.
52+
ErrX509ChainMissing = errors.New("no x5c header found")
5253

5354
// ErrNoCertsInHeaders indicates that no x5t or x5t#S256 header was found in the provided metadata.
5455
ErrNoCertsInHeaders = errors.New("no x5t or x5t#S256 header found")
@@ -76,11 +77,14 @@ type X509DidPolicy struct {
7677
Value string
7778
}
7879

79-
// X509DidReference represents a reference for an X.509 Decentralized Identifier (DID) including method, root certificate, and policies.
80+
// X509DidReference represents a reference for an X.509 Decentralized Identifier (DID).
8081
type X509DidReference struct {
81-
Method HashAlgorithm
82-
RootCertRef string
83-
Policies []X509DidPolicy
82+
// Method specifies the hash algorithm that was used to generate CAFingerprint from the raw DER bytes of the CA certificate.
83+
Method HashAlgorithm
84+
// CAFingerprint is the fingerprint of the CA certificate.
85+
CAFingerprint string
86+
// Policies contain the fields that are included in the did:x509, which must be validated against the certificates.
87+
Policies []X509DidPolicy
8488
}
8589

8690
// Resolve resolves a DID document given its identifier and corresponding metadata.
@@ -107,14 +111,17 @@ func (r Resolver) Resolve(id did.DID, metadata *resolver.ResolveMetadata) (*did.
107111
if err != nil {
108112
return nil, nil, fmt.Errorf("did:x509 x5c certificate parsing: %w", err)
109113
}
110-
_, err = findCertificateByHash(chain, ref.RootCertRef, ref.Method)
114+
caFingerprintCert, err := findCertificateByHash(chain, ref.CAFingerprint, ref.Method)
111115
if err != nil {
112116
return nil, nil, err
113117
}
114118
validationCert, err := findValidationCertificate(metadata, chain)
115119
if err != nil {
116120
return nil, nil, err
117121
}
122+
if bytes.Equal(caFingerprintCert.Raw, validationCert.Raw) {
123+
return nil, nil, fmt.Errorf("did:x509 ca-fingerprint refers to leaf certificate, must be either root or intermediate CA certificate")
124+
}
118125

119126
// Validate certificate chain, checking signatures and whether the chain is complete
120127
var chainWithoutLeaf []*x509.Certificate
@@ -225,7 +232,7 @@ func parseX509Did(id did.DID) (*X509DidReference, error) {
225232
return nil, ErrDidVersion
226233
}
227234
ref.Method = HashAlgorithm(didParts[1])
228-
ref.RootCertRef = didParts[2]
235+
ref.CAFingerprint = didParts[2]
229236

230237
for _, policyString := range policyStrings {
231238
policyFragments := strings.Split(policyString, ":")

0 commit comments

Comments
 (0)