Skip to content
This repository was archived by the owner on Apr 25, 2025. It is now read-only.

Commit 4b6b3f8

Browse files
committed
[FAB-11135] improving certpool peformance
Improved performance by loading system cert pool only once. Split Get(...certs) to separate function (Add & Get) for adding certs to certpool and getting updated certpool. go test -bench=. goos: linux goarch: amd64 pkg: github.com/hyperledger/fabric-sdk-go/pkg/core/config/comm/tls BenchmarkTLSCertPool-6 50000000 20.5 ns/op BenchmarkTLSCertPoolSameCert-6 5000000 264 ns/op BenchmarkTLSCertPoolDifferentCert-6 500000 3223 ns/op PASS ok github.com/hyperledger/fabric-sdk-go/pkg/core/config/comm/tls 9.902s Change-Id: Icb98a463de05885830efad9ff72a686e025e5e6a Signed-off-by: Sudesh Shetty <sudesh.shetty@securekey.com>
1 parent 0d62377 commit 4b6b3f8

File tree

9 files changed

+402
-80
lines changed

9 files changed

+402
-80
lines changed

pkg/common/providers/fab/provider.go

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,5 +161,8 @@ type Providers interface {
161161
// cert pool implementation.
162162
type CertPool interface {
163163
// Get returns the cert pool, optionally adding the provided certs
164-
Get(certs ...*x509.Certificate) (*x509.CertPool, error)
164+
Get() (*x509.CertPool, error)
165+
//Add allows adding certificates to CertPool
166+
//Call Get() after Add() to get the updated certpool
167+
Add(certs ...*x509.Certificate)
165168
}

pkg/common/providers/test/mockfab/mockconfig.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,11 @@ type MockCertPool struct {
7373
}
7474

7575
//Get mock implementation of fab CertPool.Get()
76-
func (c *MockCertPool) Get(certs ...*x509.Certificate) (*x509.CertPool, error) {
76+
func (c *MockCertPool) Get() (*x509.CertPool, error) {
7777
return c.CertPool, c.Err
7878
}
79+
80+
//Add mock impl of adding certs to cert pool queue
81+
func (c *MockCertPool) Add(certs ...*x509.Certificate) {
82+
83+
}

pkg/core/config/comm/comm.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,16 +19,14 @@ import (
1919
// certs for mutual TLS, and server host override. Works with certs loaded either from a path or embedded pem.
2020
func TLSConfig(cert *x509.Certificate, serverName string, config fab.EndpointConfig) (*tls.Config, error) {
2121

22-
certPool, err := config.TLSCACertPool().Get(cert)
23-
if err != nil {
24-
return nil, err
22+
if cert != nil {
23+
config.TLSCACertPool().Add(cert)
2524
}
2625

27-
if certPool == nil || len(certPool.Subjects()) == 0 {
28-
//Return empty tls config if certpool is unavailable
29-
return &tls.Config{}, nil
26+
certPool, err := config.TLSCACertPool().Get()
27+
if err != nil {
28+
return nil, err
3029
}
31-
3230
return &tls.Config{RootCAs: certPool, Certificates: config.TLSClientCerts(), ServerName: serverName}, nil
3331
}
3432

pkg/core/config/comm/tls/certpool.go

Lines changed: 86 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ import (
1010
"crypto/x509"
1111
"sync"
1212

13+
"sync/atomic"
14+
1315
"github.com/hyperledger/fabric-sdk-go/pkg/common/logging"
1416
"github.com/hyperledger/fabric-sdk-go/pkg/common/providers/fab"
1517
)
@@ -20,72 +22,114 @@ var logger = logging.NewLogger("fabsdk/core")
2022
// cert pool implementation.
2123
// It optionally allows loading the system trust store.
2224
type certPool struct {
23-
useSystemCertPool bool
24-
certs []*x509.Certificate
25-
certsByName map[string][]int
26-
lock sync.RWMutex
25+
certPool *x509.CertPool
26+
certs []*x509.Certificate
27+
certsByName map[string][]int
28+
lock sync.Mutex
29+
dirty int32
30+
certQueue []*x509.Certificate
2731
}
2832

2933
// NewCertPool new CertPool implementation
30-
func NewCertPool(useSystemCertPool bool) fab.CertPool {
31-
return &certPool{
32-
useSystemCertPool: useSystemCertPool,
33-
certsByName: make(map[string][]int),
34+
func NewCertPool(useSystemCertPool bool) (fab.CertPool, error) {
35+
36+
c, err := loadSystemCertPool(useSystemCertPool)
37+
if err != nil {
38+
return nil, err
39+
}
40+
41+
newCertPool := &certPool{
42+
certsByName: make(map[string][]int),
43+
certPool: c,
3444
}
45+
46+
return newCertPool, nil
3547
}
3648

37-
func (c *certPool) Get(certs ...*x509.Certificate) (*x509.CertPool, error) {
49+
//Get returns certpool
50+
//if there are any certs in cert queue added by any previous Add() call, it adds those certs to certpool before returning
51+
func (c *certPool) Get() (*x509.CertPool, error) {
52+
53+
//if dirty then add certs from queue to cert pool
54+
if atomic.CompareAndSwapInt32(&c.dirty, 1, 0) {
3855

39-
if len(certs) > 0 {
4056
c.lock.Lock()
41-
//add certs to SDK cert list
42-
for _, newCert := range certs {
43-
c.addCert(newCert)
57+
defer c.lock.Unlock()
58+
59+
//add all new certs in queue to cert pool
60+
for _, cert := range c.certQueue {
61+
c.certPool.AddCert(cert)
4462
}
45-
c.lock.Unlock()
63+
c.certQueue = []*x509.Certificate{}
4664
}
4765

48-
c.lock.RLock()
49-
defer c.lock.RUnlock()
66+
return c.certPool, nil
67+
}
5068

51-
// create the cert pool
52-
certPool, err := c.loadSystemCertPool()
53-
if err != nil {
54-
return nil, err
69+
//Add adds given certs to cert pool queue, those certs will be added to certpool during subsequent Get() call
70+
func (c *certPool) Add(certs ...*x509.Certificate) {
71+
if len(certs) == 0 {
72+
return
5573
}
5674

57-
//add all certs to cert pool
58-
for _, cert := range c.certs {
59-
certPool.AddCert(cert)
60-
}
75+
c.lock.Lock()
76+
defer c.lock.Unlock()
6177

62-
return certPool, nil
63-
}
78+
//filter certs to be added, check if they already exist or duplicate
79+
certsToBeAdded := c.filterCerts(certs...)
80+
81+
if len(certsToBeAdded) > 0 {
6482

65-
func (c *certPool) addCert(newCert *x509.Certificate) {
66-
if newCert != nil && !c.containsCert(newCert) {
67-
n := len(c.certs)
68-
// Store cert
69-
c.certs = append(c.certs, newCert)
70-
// Store cert name index
71-
name := string(newCert.RawSubject)
72-
c.certsByName[name] = append(c.certsByName[name], n)
83+
for _, newCert := range certsToBeAdded {
84+
c.certQueue = append(c.certQueue, newCert)
85+
// Store cert name index
86+
name := string(newCert.RawSubject)
87+
c.certsByName[name] = append(c.certsByName[name], len(c.certs))
88+
// Store cert
89+
c.certs = append(c.certs, newCert)
90+
}
91+
92+
atomic.CompareAndSwapInt32(&c.dirty, 0, 1)
7393
}
7494
}
7595

76-
func (c *certPool) containsCert(newCert *x509.Certificate) bool {
77-
possibilities := c.certsByName[string(newCert.RawSubject)]
78-
for _, p := range possibilities {
79-
if c.certs[p].Equal(newCert) {
80-
return true
96+
//filterCerts remove certs from list if they already exist in pool or duplicate
97+
func (c *certPool) filterCerts(certs ...*x509.Certificate) []*x509.Certificate {
98+
filtered := []*x509.Certificate{}
99+
100+
CertLoop:
101+
for _, cert := range certs {
102+
if cert == nil {
103+
continue
81104
}
105+
possibilities := c.certsByName[string(cert.RawSubject)]
106+
for _, p := range possibilities {
107+
if c.certs[p].Equal(cert) {
108+
continue CertLoop
109+
}
110+
}
111+
filtered = append(filtered, cert)
82112
}
83113

84-
return false
114+
//remove duplicate from list of certs being passed
115+
return removeDuplicates(filtered...)
116+
}
117+
118+
func removeDuplicates(certs ...*x509.Certificate) []*x509.Certificate {
119+
encountered := map[*x509.Certificate]bool{}
120+
result := []*x509.Certificate{}
121+
122+
for v := range certs {
123+
if !encountered[certs[v]] {
124+
encountered[certs[v]] = true
125+
result = append(result, certs[v])
126+
}
127+
}
128+
return result
85129
}
86130

87-
func (c *certPool) loadSystemCertPool() (*x509.CertPool, error) {
88-
if !c.useSystemCertPool {
131+
func loadSystemCertPool(useSystemCertPool bool) (*x509.CertPool, error) {
132+
if !useSystemCertPool {
89133
return x509.NewCertPool(), nil
90134
}
91135
systemCertPool, err := x509.SystemCertPool()

0 commit comments

Comments
 (0)