Skip to content

Commit 440fb9a

Browse files
committed
validate time before sign the identity certificate (#329)
1 parent 45415c6 commit 440fb9a

File tree

3 files changed

+156
-2
lines changed

3 files changed

+156
-2
lines changed

client/client_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ func NewTestSecureClient() (*client.Client, error) {
7171
func NewTestSecureClientWithGeneratedCertificate() (*client.Client, error) {
7272
var cfgCA generateCertificate.Configuration
7373
cfgCA.Subject.CommonName = "anotherClient"
74+
cfgCA.ValidFrom = "now"
75+
cfgCA.ValidFor = time.Hour
7476

7577
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
7678
if err != nil {

pkg/security/signer/signer.go

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,27 @@ func NewOCFIdentityCertificate(caCert []*x509.Certificate, caKey crypto.PrivateK
4343
}
4444

4545
func (s *OCFIdentityCertificate) Sign(ctx context.Context, csr []byte) (signedCsr []byte, err error) {
46+
now := time.Now()
47+
notBefore := s.validNotBefore
48+
notAfter := s.validNotAfter
49+
for _, c := range s.caCert {
50+
if notBefore.Before(c.NotBefore) {
51+
notBefore = c.NotBefore
52+
}
53+
if notAfter.After(c.NotAfter) {
54+
notAfter = c.NotAfter
55+
}
56+
}
57+
if notBefore.After(notAfter) {
58+
return nil, fmt.Errorf("invalid time range: not before %v limit is greater than not after limit %v", notBefore.Format(time.RFC3339), notAfter.Format(time.RFC3339))
59+
}
60+
if now.Before(notBefore) {
61+
return nil, fmt.Errorf("not valid yet: current time %v is out of time range: %v <-> %v", now, notBefore.Format(time.RFC3339), notAfter.Format(time.RFC3339))
62+
}
63+
if now.After(notAfter) {
64+
return nil, fmt.Errorf("expired: current time %v is out of time range: %v <-> %v", now, notBefore.Format(time.RFC3339), notAfter.Format(time.RFC3339))
65+
}
66+
4667
csrBlock, _ := pem.Decode(csr)
4768
if csrBlock == nil {
4869
err = fmt.Errorf("pem not found")
@@ -59,8 +80,6 @@ func (s *OCFIdentityCertificate) Sign(ctx context.Context, csr []byte) (signedCs
5980
return
6081
}
6182

62-
notBefore := s.validNotBefore
63-
notAfter := s.validNotAfter
6483
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
6584
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
6685
if err != nil {

pkg/security/signer/signer_test.go

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
package signer
2+
3+
import (
4+
"context"
5+
"crypto"
6+
"crypto/ecdsa"
7+
"crypto/elliptic"
8+
"crypto/rand"
9+
"crypto/x509"
10+
"os"
11+
"testing"
12+
"time"
13+
14+
"github.com/google/uuid"
15+
"github.com/plgd-dev/kit/v2/security"
16+
"github.com/plgd-dev/kit/v2/security/generateCertificate"
17+
"github.com/stretchr/testify/require"
18+
)
19+
20+
func TestOCFIdentityCertificate_Sign(t *testing.T) {
21+
type fields struct {
22+
caCert []*x509.Certificate
23+
caKey crypto.PrivateKey
24+
validNotBefore time.Time
25+
validNotAfter time.Time
26+
}
27+
type args struct {
28+
ctx context.Context
29+
csr []byte
30+
}
31+
caCert, err := security.LoadX509(os.Getenv("ROOT_CA_CRT"))
32+
require.NoError(t, err)
33+
caKey, err := security.LoadX509PrivateKey(os.Getenv("ROOT_CA_KEY"))
34+
require.NoError(t, err)
35+
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
36+
require.NoError(t, err)
37+
id := uuid.NewString()
38+
csr, err := generateCertificate.GenerateIdentityCSR(generateCertificate.Configuration{}, id, priv)
39+
require.NoError(t, err)
40+
41+
tests := []struct {
42+
name string
43+
fields fields
44+
args args
45+
wantErr bool
46+
}{
47+
{
48+
name: "valid",
49+
fields: fields{
50+
caCert: caCert,
51+
caKey: caKey,
52+
validNotBefore: time.Now().Add(-time.Second),
53+
validNotAfter: time.Now().Add(time.Hour),
54+
},
55+
args: args{
56+
ctx: context.Background(),
57+
csr: csr,
58+
},
59+
},
60+
{
61+
name: "valid - bigger time range than ca signer chain supported",
62+
fields: fields{
63+
caCert: caCert,
64+
caKey: caKey,
65+
validNotBefore: time.Now().Add(-time.Hour * 8760 * 10),
66+
validNotAfter: time.Now().Add(time.Hour * 8760 * 10),
67+
},
68+
args: args{
69+
ctx: context.Background(),
70+
csr: csr,
71+
},
72+
},
73+
{
74+
name: "invalid time range",
75+
fields: fields{
76+
caCert: caCert,
77+
caKey: caKey,
78+
validNotBefore: time.Now().Add(time.Hour),
79+
validNotAfter: time.Now().Add(-time.Second),
80+
},
81+
args: args{
82+
ctx: context.Background(),
83+
csr: csr,
84+
},
85+
wantErr: true,
86+
},
87+
{
88+
name: "invalid time range - now before not before",
89+
fields: fields{
90+
caCert: caCert,
91+
caKey: caKey,
92+
validNotBefore: time.Now().Add(time.Hour),
93+
validNotAfter: time.Now().Add(time.Hour * 2),
94+
},
95+
args: args{
96+
ctx: context.Background(),
97+
csr: csr,
98+
},
99+
wantErr: true,
100+
},
101+
{
102+
name: "invalid time range - now after not after",
103+
fields: fields{
104+
caCert: caCert,
105+
caKey: caKey,
106+
validNotBefore: time.Now(),
107+
validNotAfter: time.Now().Add(time.Nanosecond),
108+
},
109+
args: args{
110+
ctx: context.Background(),
111+
csr: csr,
112+
},
113+
wantErr: true,
114+
},
115+
}
116+
for _, tt := range tests {
117+
t.Run(tt.name, func(t *testing.T) {
118+
s := &OCFIdentityCertificate{
119+
caCert: tt.fields.caCert,
120+
caKey: tt.fields.caKey,
121+
validNotBefore: tt.fields.validNotBefore,
122+
validNotAfter: tt.fields.validNotAfter,
123+
}
124+
gotSignedCsr, err := s.Sign(tt.args.ctx, tt.args.csr)
125+
if tt.wantErr {
126+
require.Error(t, err)
127+
return
128+
}
129+
require.NoError(t, err)
130+
require.NotEmpty(t, gotSignedCsr)
131+
})
132+
}
133+
}

0 commit comments

Comments
 (0)