1 package nonce
2
3 import (
4 "context"
5 "errors"
6 "fmt"
7 "testing"
8
9 "github.com/letsencrypt/boulder/metrics"
10 noncepb "github.com/letsencrypt/boulder/nonce/proto"
11 "github.com/letsencrypt/boulder/test"
12 "google.golang.org/grpc"
13 )
14
15 func TestImplementation(t *testing.T) {
16 test.AssertImplementsGRPCServer(t, &Server{}, noncepb.UnimplementedNonceServiceServer{})
17 }
18
19 func TestValidNonce(t *testing.T) {
20 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
21 test.AssertNotError(t, err, "Could not create nonce service")
22 n, err := ns.Nonce()
23 test.AssertNotError(t, err, "Could not create nonce")
24 test.Assert(t, ns.Valid(n), fmt.Sprintf("Did not recognize fresh nonce %s", n))
25 }
26
27 func TestAlreadyUsed(t *testing.T) {
28 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
29 test.AssertNotError(t, err, "Could not create nonce service")
30 n, err := ns.Nonce()
31 test.AssertNotError(t, err, "Could not create nonce")
32 test.Assert(t, ns.Valid(n), "Did not recognize fresh nonce")
33 test.Assert(t, !ns.Valid(n), "Recognized the same nonce twice")
34 }
35
36 func TestRejectMalformed(t *testing.T) {
37 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
38 test.AssertNotError(t, err, "Could not create nonce service")
39 n, err := ns.Nonce()
40 test.AssertNotError(t, err, "Could not create nonce")
41 test.Assert(t, !ns.Valid("asdf"+n), "Accepted an invalid nonce")
42 }
43
44 func TestRejectShort(t *testing.T) {
45 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
46 test.AssertNotError(t, err, "Could not create nonce service")
47 test.Assert(t, !ns.Valid("aGkK"), "Accepted an invalid nonce")
48 }
49
50 func TestRejectUnknown(t *testing.T) {
51 ns1, err := NewNonceService(metrics.NoopRegisterer, 0, "")
52 test.AssertNotError(t, err, "Could not create nonce service")
53 ns2, err := NewNonceService(metrics.NoopRegisterer, 0, "")
54 test.AssertNotError(t, err, "Could not create nonce service")
55
56 n, err := ns1.Nonce()
57 test.AssertNotError(t, err, "Could not create nonce")
58 test.Assert(t, !ns2.Valid(n), "Accepted a foreign nonce")
59 }
60
61 func TestRejectTooLate(t *testing.T) {
62 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
63 test.AssertNotError(t, err, "Could not create nonce service")
64
65 ns.latest = 2
66 n, err := ns.Nonce()
67 test.AssertNotError(t, err, "Could not create nonce")
68 ns.latest = 1
69 test.Assert(t, !ns.Valid(n), "Accepted a nonce with a too-high counter")
70 }
71
72 func TestRejectTooEarly(t *testing.T) {
73 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
74 test.AssertNotError(t, err, "Could not create nonce service")
75
76 n0, err := ns.Nonce()
77 test.AssertNotError(t, err, "Could not create nonce")
78
79 for i := 0; i < ns.maxUsed; i++ {
80 n, err := ns.Nonce()
81 test.AssertNotError(t, err, "Could not create nonce")
82 if !ns.Valid(n) {
83 t.Errorf("generated invalid nonce")
84 }
85 }
86
87 n1, err := ns.Nonce()
88 test.AssertNotError(t, err, "Could not create nonce")
89 n2, err := ns.Nonce()
90 test.AssertNotError(t, err, "Could not create nonce")
91 n3, err := ns.Nonce()
92 test.AssertNotError(t, err, "Could not create nonce")
93
94 test.Assert(t, ns.Valid(n3), "Rejected a valid nonce")
95 test.Assert(t, ns.Valid(n2), "Rejected a valid nonce")
96 test.Assert(t, ns.Valid(n1), "Rejected a valid nonce")
97 test.Assert(t, !ns.Valid(n0), "Accepted a nonce that we should have forgotten")
98 }
99
100 func BenchmarkNonces(b *testing.B) {
101 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "")
102 if err != nil {
103 b.Fatal("creating nonce service", err)
104 }
105
106 for i := 0; i < ns.maxUsed; i++ {
107 n, err := ns.Nonce()
108 if err != nil {
109 b.Fatal("noncing", err)
110 }
111 if !ns.Valid(n) {
112 b.Fatal("generated invalid nonce")
113 }
114 }
115
116 b.ResetTimer()
117 b.RunParallel(func(pb *testing.PB) {
118 for pb.Next() {
119 n, err := ns.Nonce()
120 if err != nil {
121 b.Fatal("noncing", err)
122 }
123 if !ns.Valid(n) {
124 b.Fatal("generated invalid nonce")
125 }
126 }
127 })
128 }
129
130 func TestNoncePrefixing(t *testing.T) {
131 ns, err := NewNonceService(metrics.NoopRegisterer, 0, "zinc")
132 test.AssertNotError(t, err, "Could not create nonce service")
133
134 n, err := ns.Nonce()
135 test.AssertNotError(t, err, "Could not create nonce")
136 test.Assert(t, ns.Valid(n), "Valid nonce rejected")
137
138 n, err = ns.Nonce()
139 test.AssertNotError(t, err, "Could not create nonce")
140 n = n[1:]
141 test.Assert(t, !ns.Valid(n), "Valid nonce with incorrect prefix accepted")
142
143 n, err = ns.Nonce()
144 test.AssertNotError(t, err, "Could not create nonce")
145 test.Assert(t, !ns.Valid(n[6:]), "Valid nonce without prefix accepted")
146 }
147
148 type validRedeemer struct{}
149
150 func (vr *validRedeemer) Redeem(ctx context.Context, in *noncepb.NonceMessage, _ ...grpc.CallOption) (*noncepb.ValidMessage, error) {
151 return &noncepb.ValidMessage{Valid: true}, nil
152 }
153
154 type invalidRedeemer struct{}
155
156 func (ivr *invalidRedeemer) Redeem(ctx context.Context, in *noncepb.NonceMessage, _ ...grpc.CallOption) (*noncepb.ValidMessage, error) {
157 return &noncepb.ValidMessage{Valid: false}, nil
158 }
159
160 type brokenRedeemer struct{}
161
162 func (br *brokenRedeemer) Redeem(ctx context.Context, in *noncepb.NonceMessage, _ ...grpc.CallOption) (*noncepb.ValidMessage, error) {
163 return nil, errors.New("broken redeemer!")
164 }
165
166 func TestRemoteRedeem(t *testing.T) {
167 valid, err := RemoteRedeem(context.Background(), nil, "q")
168 test.AssertNotError(t, err, "RemoteRedeem failed")
169 test.Assert(t, !valid, "RemoteRedeem accepted an invalid nonce")
170 valid, err = RemoteRedeem(context.Background(), nil, "")
171 test.AssertNotError(t, err, "RemoteRedeem failed")
172 test.Assert(t, !valid, "RemoteRedeem accepted an empty nonce")
173
174 prefixMap := map[string]Redeemer{
175 "abcd": &brokenRedeemer{},
176 "wxyz": &invalidRedeemer{},
177 }
178
179 valid, err = RemoteRedeem(context.Background(), prefixMap, "asddCQEC")
180 test.AssertNotError(t, err, "RemoteRedeem failed")
181 test.Assert(t, !valid, "RemoteRedeem accepted nonce not in prefix map")
182
183
184
185 _, err = RemoteRedeem(context.Background(), prefixMap, "abcdbeef")
186 test.AssertError(t, err, "RemoteRedeem didn't return error when remote did")
187
188
189
190 valid, err = RemoteRedeem(context.Background(), prefixMap, "wxyzdead")
191 test.AssertNotError(t, err, "RemoteRedeem failed")
192 test.Assert(t, !valid, "RemoteRedeem didn't honor remote result")
193
194
195
196 prefixMap["wxyz"] = &validRedeemer{}
197 valid, err = RemoteRedeem(context.Background(), prefixMap, "wxyzdead")
198 test.AssertNotError(t, err, "RemoteRedeem failed")
199 test.Assert(t, valid, "RemoteRedeem didn't honor remote result")
200 }
201
202 func TestNoncePrefixValidation(t *testing.T) {
203 _, err := NewNonceService(metrics.NoopRegisterer, 0, "hey")
204 test.AssertError(t, err, "NewNonceService didn't fail with short prefix")
205 _, err = NewNonceService(metrics.NoopRegisterer, 0, "hey!")
206 test.AssertError(t, err, "NewNonceService didn't fail with invalid base64")
207 _, err = NewNonceService(metrics.NoopRegisterer, 0, "heyy")
208 test.AssertNotError(t, err, "NewNonceService failed with valid nonce prefix")
209 }
210
211 func TestDerivePrefix(t *testing.T) {
212 prefix := DerivePrefix("192.168.1.1:8080", "3b8c758dd85e113ea340ce0b3a99f389d40a308548af94d1730a7692c1874f1f")
213 test.AssertEquals(t, prefix, "P9qQaK4o")
214 }
215
View as plain text