File tree 5 files changed +54
-1
lines changed
5 files changed +54
-1
lines changed Original file line number Diff line number Diff line change 1
1
all : check build
2
2
3
3
test :
4
- go test -v ./...
4
+ go test --race - v ./...
5
5
6
6
check : test
7
7
golangci-lint run
Original file line number Diff line number Diff line change @@ -30,6 +30,7 @@ import (
30
30
"net/url"
31
31
"os"
32
32
"strings"
33
+ "sync"
33
34
"time"
34
35
35
36
"github.com/tsaarni/x500dn"
@@ -94,6 +95,10 @@ type Certificate struct {
94
95
// GeneratedCert is a pointer to the generated certificate and private key.
95
96
// It is automatically set after calling any of the Certificate functions.
96
97
GeneratedCert * tls.Certificate `json:"-" hash:"-"`
98
+
99
+ // lazyInitialize ensures that only single goroutine can run lazy initialization of certificate concurrently.
100
+ // Concurrent regeneration of certificate and private key by explicit call to Generate() is not supported.
101
+ lazyInitialize sync.Mutex
97
102
}
98
103
99
104
type KeyType uint
@@ -245,6 +250,9 @@ func (c *Certificate) defaults() error {
245
250
}
246
251
247
252
func (c * Certificate ) ensureGenerated () error {
253
+ c .lazyInitialize .Lock ()
254
+ defer c .lazyInitialize .Unlock ()
255
+
248
256
if c .GeneratedCert == nil {
249
257
err := c .Generate ()
250
258
if err != nil {
Original file line number Diff line number Diff line change @@ -26,6 +26,7 @@ import (
26
26
"net/url"
27
27
"os"
28
28
"path"
29
+ "sync"
29
30
"testing"
30
31
"time"
31
32
@@ -401,3 +402,20 @@ func TestCertificateChainInPEM(t *testing.T) {
401
402
402
403
assert .Empty (t , rest )
403
404
}
405
+
406
+ func TestParallelCertificateLazyInitialization (t * testing.T ) {
407
+ cert := Certificate {Subject : "CN=Joe" }
408
+
409
+ // Trigger lazy initialization by calling one of the generator methods in parallel.
410
+ var wg sync.WaitGroup
411
+ for i := 0 ; i < 10 ; i ++ {
412
+ wg .Add (1 )
413
+ go func (cert * Certificate ) {
414
+ defer wg .Done ()
415
+ _ , err := cert .X509Certificate ()
416
+ assert .Nil (t , err )
417
+ }(& cert )
418
+ }
419
+
420
+ wg .Wait ()
421
+ }
Original file line number Diff line number Diff line change @@ -23,6 +23,7 @@ import (
23
23
"fmt"
24
24
"math/big"
25
25
"os"
26
+ "sync"
26
27
"time"
27
28
)
28
29
@@ -44,6 +45,9 @@ type CRL struct {
44
45
// Issuer is the CA certificate issuing this CRL.
45
46
// If not set, it defaults to the issuer of certificates added to Revoked list.
46
47
Issuer * Certificate
48
+
49
+ // mutex ensures that only single goroutine can generate CRL concurrently.
50
+ mutex sync.Mutex
47
51
}
48
52
49
53
// Add appends a Certificate to CRL list.
@@ -64,6 +68,9 @@ func (crl *CRL) Add(cert *Certificate) error {
64
68
// DER returns the CRL as DER buffer.
65
69
// Error is not nil if generation fails.
66
70
func (crl * CRL ) DER () (crlBytes []byte , err error ) {
71
+ crl .mutex .Lock ()
72
+ defer crl .mutex .Unlock ()
73
+
67
74
if crl .Issuer == nil {
68
75
if len (crl .Revoked ) == 0 {
69
76
return nil , fmt .Errorf ("issuer not known: either set Issuer or add certificates to the CRL" )
Original file line number Diff line number Diff line change @@ -17,6 +17,7 @@ package certyaml
17
17
import (
18
18
"crypto/x509"
19
19
"math/big"
20
+ "sync"
20
21
"testing"
21
22
22
23
"github.com/stretchr/testify/assert"
@@ -98,3 +99,22 @@ func TestEmptyCRL(t *testing.T) {
98
99
_ , err = crl .DER ()
99
100
assert .NotNil (t , err )
100
101
}
102
+
103
+ func TestParallelCRLLazyInitialization (t * testing.T ) {
104
+ ca := Certificate {Subject : "CN=ca" }
105
+ revoked := Certificate {Subject : "CN=Joe" , Issuer : & ca }
106
+ crl := CRL {Revoked : []* Certificate {& revoked }}
107
+
108
+ // Call CRL generation in parallel.
109
+ var wg sync.WaitGroup
110
+ for i := 0 ; i < 10 ; i ++ {
111
+ wg .Add (1 )
112
+ go func (cert * Certificate ) {
113
+ defer wg .Done ()
114
+ _ , err := crl .DER ()
115
+ assert .Nil (t , err )
116
+ }(& ca )
117
+ }
118
+
119
+ wg .Wait ()
120
+ }
You can’t perform that action at this time.
0 commit comments