1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
|
package goodkey
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rsa"
"errors"
"fmt"
"math/big"
"sync"
"github.com/letsencrypt/boulder/core"
berrors "github.com/letsencrypt/boulder/errors"
"github.com/letsencrypt/boulder/features"
sapb "github.com/letsencrypt/boulder/sa/proto"
"google.golang.org/grpc"
"github.com/titanous/rocacheck"
)
// To generate, run: primes 2 752 | tr '\n' ,
var smallPrimeInts = []int64{
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47,
53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107,
109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167,
173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229,
233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283,
293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359,
367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431,
433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491,
499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571,
577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641,
643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709,
719, 727, 733, 739, 743, 751,
}
// singleton defines the object of a Singleton pattern
var (
smallPrimesSingleton sync.Once
smallPrimesProduct *big.Int
)
type Config struct {
// WeakKeyFile is the path to a JSON file containing truncated modulus hashes
// of known weak RSA keys. If this config value is empty, then RSA modulus
// hash checking will be disabled.
WeakKeyFile string
// BlockedKeyFile is the path to a YAML file containing base64-encoded SHA256
// hashes of PKIX Subject Public Keys that should be blocked. If this config
// value is empty, then blocked key checking will be disabled.
BlockedKeyFile string
// FermatRounds is an integer number of rounds of Fermat's factorization
// method that should be performed to attempt to detect keys whose modulus can
// be trivially factored because the two factors are very close to each other.
// If this config value is empty (0), no factorization will be attempted.
FermatRounds int
}
// ErrBadKey represents an error with a key. It is distinct from the various
// ways in which an ACME request can have an erroneous key (BadPublicKeyError,
// BadCSRError) because this library is used to check both JWS signing keys and
// keys in CSRs.
var ErrBadKey = errors.New("")
func badKey(msg string, args ...interface{}) error {
return fmt.Errorf("%w%s", ErrBadKey, fmt.Errorf(msg, args...))
}
// BlockedKeyCheckFunc is used to pass in the sa.BlockedKey method to KeyPolicy,
// rather than storing a full sa.SQLStorageAuthority. This makes testing
// significantly simpler.
type BlockedKeyCheckFunc func(context.Context, *sapb.KeyBlockedRequest, ...grpc.CallOption) (*sapb.Exists, error)
// KeyPolicy determines which types of key may be used with various boulder
// operations.
type KeyPolicy struct {
AllowRSA bool // Whether RSA keys should be allowed.
AllowECDSANISTP256 bool // Whether ECDSA NISTP256 keys should be allowed.
AllowECDSANISTP384 bool // Whether ECDSA NISTP384 keys should be allowed.
weakRSAList *WeakRSAKeys
blockedList *blockedKeys
fermatRounds int
dbCheck BlockedKeyCheckFunc
}
// NewKeyPolicy returns a KeyPolicy that allows RSA, ECDSA256 and ECDSA384.
// weakKeyFile contains the path to a JSON file containing truncated modulus
// hashes of known weak RSA keys. If this argument is empty RSA modulus hash
// checking will be disabled. blockedKeyFile contains the path to a YAML file
// containing Base64 encoded SHA256 hashes of pkix subject public keys that
// should be blocked. If this argument is empty then no blocked key checking is
// performed.
func NewKeyPolicy(config *Config, bkc BlockedKeyCheckFunc) (KeyPolicy, error) {
kp := KeyPolicy{
AllowRSA: true,
AllowECDSANISTP256: true,
AllowECDSANISTP384: true,
dbCheck: bkc,
}
if config.WeakKeyFile != "" {
keyList, err := LoadWeakRSASuffixes(config.WeakKeyFile)
if err != nil {
return KeyPolicy{}, err
}
kp.weakRSAList = keyList
}
if config.BlockedKeyFile != "" {
blocked, err := loadBlockedKeysList(config.BlockedKeyFile)
if err != nil {
return KeyPolicy{}, err
}
kp.blockedList = blocked
}
if config.FermatRounds < 0 {
return KeyPolicy{}, fmt.Errorf("Fermat factorization rounds cannot be negative: %d", config.FermatRounds)
}
kp.fermatRounds = config.FermatRounds
return kp, nil
}
// GoodKey returns true if the key is acceptable for both TLS use and account
// key use (our requirements are the same for either one), according to basic
// strength and algorithm checking. GoodKey only supports pointers: *rsa.PublicKey
// and *ecdsa.PublicKey. It will reject non-pointer types.
// TODO: Support JSONWebKeys once go-jose migration is done.
func (policy *KeyPolicy) GoodKey(ctx context.Context, key crypto.PublicKey) error {
// Early rejection of unacceptable key types to guard subsequent checks.
switch t := key.(type) {
case *rsa.PublicKey, *ecdsa.PublicKey:
break
default:
return badKey("unsupported key type %T", t)
}
// If there is a blocked list configured then check if the public key is one
// that has been administratively blocked.
if policy.blockedList != nil {
if blocked, err := policy.blockedList.blocked(key); err != nil {
return berrors.InternalServerError("error checking blocklist for key: %v", key)
} else if blocked {
return badKey("public key is forbidden")
}
}
if policy.dbCheck != nil {
digest, err := core.KeyDigest(key)
if err != nil {
return badKey("%w", err)
}
exists, err := policy.dbCheck(ctx, &sapb.KeyBlockedRequest{KeyHash: digest[:]})
if err != nil {
return err
} else if exists.Exists {
return badKey("public key is forbidden")
}
}
switch t := key.(type) {
case *rsa.PublicKey:
return policy.goodKeyRSA(t)
case *ecdsa.PublicKey:
return policy.goodKeyECDSA(t)
default:
return badKey("unsupported key type %T", key)
}
}
// GoodKeyECDSA determines if an ECDSA pubkey meets our requirements
func (policy *KeyPolicy) goodKeyECDSA(key *ecdsa.PublicKey) (err error) {
// Check the curve.
//
// The validity of the curve is an assumption for all following tests.
err = policy.goodCurve(key.Curve)
if err != nil {
return err
}
// Key validation routine adapted from NIST SP800-56A § 5.6.2.3.2.
// <http://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-56Ar2.pdf>
//
// Assuming a prime field since a) we are only allowing such curves and b)
// crypto/elliptic only supports prime curves. Where this assumption
// simplifies the code below, it is explicitly stated and explained. If ever
// adapting this code to support non-prime curves, refer to NIST SP800-56A §
// 5.6.2.3.2 and adapt this code appropriately.
params := key.Params()
// SP800-56A § 5.6.2.3.2 Step 1.
// Partial check of the public key for an invalid range in the EC group:
// Verify that key is not the point at infinity O.
// This code assumes that the point at infinity is (0,0), which is the
// case for all supported curves.
if isPointAtInfinityNISTP(key.X, key.Y) {
return badKey("key x, y must not be the point at infinity")
}
// SP800-56A § 5.6.2.3.2 Step 2.
// "Verify that x_Q and y_Q are integers in the interval [0,p-1] in the
// case that q is an odd prime p, or that x_Q and y_Q are bit strings
// of length m bits in the case that q = 2**m."
//
// Prove prime field: ASSUMED.
// Prove q != 2: ASSUMED. (Curve parameter. No supported curve has q == 2.)
// Prime field && q != 2 => q is an odd prime p
// Therefore "verify that x, y are in [0, p-1]" satisfies step 2.
//
// Therefore verify that both x and y of the public key point have the unique
// correct representation of an element in the underlying field by verifying
// that x and y are integers in [0, p-1].
if key.X.Sign() < 0 || key.Y.Sign() < 0 {
return badKey("key x, y must not be negative")
}
if key.X.Cmp(params.P) >= 0 || key.Y.Cmp(params.P) >= 0 {
return badKey("key x, y must not exceed P-1")
}
// SP800-56A § 5.6.2.3.2 Step 3.
// "If q is an odd prime p, verify that (y_Q)**2 === (x_Q)***3 + a*x_Q + b (mod p).
// If q = 2**m, verify that (y_Q)**2 + (x_Q)*(y_Q) == (x_Q)**3 + a*(x_Q)*2 + b in
// the finite field of size 2**m.
// (Ensures that the public key is on the correct elliptic curve.)"
//
// q is an odd prime p: proven/assumed above.
// a = -3 for all supported curves.
//
// Therefore step 3 is satisfied simply by showing that
// y**2 === x**3 - 3*x + B (mod P).
//
// This proves that the public key is on the correct elliptic curve.
// But in practice, this test is provided by crypto/elliptic, so use that.
if !key.Curve.IsOnCurve(key.X, key.Y) {
return badKey("key point is not on the curve")
}
// SP800-56A § 5.6.2.3.2 Step 4.
// "Verify that n*Q == Ø.
// (Ensures that the public key has the correct order. Along with check 1,
// ensures that the public key is in the correct range in the correct EC
// subgroup, that is, it is in the correct EC subgroup and is not the
// identity element.)"
//
// Ensure that public key has the correct order:
// verify that n*Q = Ø.
//
// n*Q = Ø iff n*Q is the point at infinity (see step 1).
ox, oy := key.Curve.ScalarMult(key.X, key.Y, params.N.Bytes())
if !isPointAtInfinityNISTP(ox, oy) {
return badKey("public key does not have correct order")
}
// End of SP800-56A § 5.6.2.3.2 Public Key Validation Routine.
// Key is valid.
return nil
}
// Returns true iff the point (x,y) on NIST P-256, NIST P-384 or NIST P-521 is
// the point at infinity. These curves all have the same point at infinity
// (0,0). This function must ONLY be used on points on curves verified to have
// (0,0) as their point at infinity.
func isPointAtInfinityNISTP(x, y *big.Int) bool {
return x.Sign() == 0 && y.Sign() == 0
}
// GoodCurve determines if an elliptic curve meets our requirements.
func (policy *KeyPolicy) goodCurve(c elliptic.Curve) (err error) {
// Simply use a whitelist for now.
params := c.Params()
switch {
case policy.AllowECDSANISTP256 && params == elliptic.P256().Params():
return nil
case policy.AllowECDSANISTP384 && params == elliptic.P384().Params():
return nil
default:
return badKey("ECDSA curve %v not allowed", params.Name)
}
}
var acceptableRSAKeySizes = map[int]bool{
2048: true,
3072: true,
4096: true,
}
// GoodKeyRSA determines if a RSA pubkey meets our requirements
func (policy *KeyPolicy) goodKeyRSA(key *rsa.PublicKey) (err error) {
if !policy.AllowRSA {
return badKey("RSA keys are not allowed")
}
if policy.weakRSAList != nil && policy.weakRSAList.Known(key) {
return badKey("key is on a known weak RSA key list")
}
// Baseline Requirements Appendix A
// Modulus must be >= 2048 bits and <= 4096 bits
modulus := key.N
modulusBitLen := modulus.BitLen()
if features.Enabled(features.RestrictRSAKeySizes) {
if !acceptableRSAKeySizes[modulusBitLen] {
return badKey("key size not supported: %d", modulusBitLen)
}
} else {
const maxKeySize = 4096
if modulusBitLen < 2048 {
return badKey("key too small: %d", modulusBitLen)
}
if modulusBitLen > maxKeySize {
return badKey("key too large: %d > %d", modulusBitLen, maxKeySize)
}
// Bit lengths that are not a multiple of 8 may cause problems on some
// client implementations.
if modulusBitLen%8 != 0 {
return badKey("key length wasn't a multiple of 8: %d", modulusBitLen)
}
}
// Rather than support arbitrary exponents, which significantly increases
// the size of the key space we allow, we restrict E to the defacto standard
// RSA exponent 65537. There is no specific standards document that specifies
// 65537 as the 'best' exponent, but ITU X.509 Annex C suggests there are
// notable merits for using it if using a fixed exponent.
//
// The CABF Baseline Requirements state:
// The CA SHALL confirm that the value of the public exponent is an
// odd number equal to 3 or more. Additionally, the public exponent
// SHOULD be in the range between 2^16 + 1 and 2^256-1.
//
// By only allowing one exponent, which fits these constraints, we satisfy
// these requirements.
if key.E != 65537 {
return badKey("key exponent must be 65537")
}
// The modulus SHOULD also have the following characteristics: an odd
// number, not the power of a prime, and have no factors smaller than 752.
// TODO: We don't yet check for "power of a prime."
if checkSmallPrimes(modulus) {
return badKey("key divisible by small prime")
}
// Check for weak keys generated by Infineon hardware
// (see https://crocs.fi.muni.cz/public/papers/rsa_ccs17)
if rocacheck.IsWeak(key) {
return badKey("key generated by vulnerable Infineon-based hardware")
}
// Check if the key can be easily factored via Fermat's factorization method.
if policy.fermatRounds > 0 {
err := checkPrimeFactorsTooClose(modulus, policy.fermatRounds)
if err != nil {
return badKey("key generated with factors too close together: %w", err)
}
}
return nil
}
// Returns true iff integer i is divisible by any of the primes in smallPrimes.
//
// Short circuits; execution time is dependent on i. Do not use this on secret
// values.
//
// Rather than checking each prime individually (invoking Mod on each),
// multiply the primes together and let GCD do our work for us: if the
// GCD between <key> and <product of primes> is not one, we know we have
// a bad key. This is substantially faster than checking each prime
// individually.
func checkSmallPrimes(i *big.Int) bool {
smallPrimesSingleton.Do(func() {
smallPrimesProduct = big.NewInt(1)
for _, prime := range smallPrimeInts {
smallPrimesProduct.Mul(smallPrimesProduct, big.NewInt(prime))
}
})
// When the GCD is 1, i and smallPrimesProduct are coprime, meaning they
// share no common factors. When the GCD is not one, it is the product of
// all common factors, meaning we've identified at least one small prime
// which invalidates i as a valid key.
var result big.Int
result.GCD(nil, nil, i, smallPrimesProduct)
return result.Cmp(big.NewInt(1)) != 0
}
// Returns an error if the modulus n is able to be factored into primes p and q
// via Fermat's factorization method. This method relies on the two primes being
// very close together, which means that they were almost certainly not picked
// independently from a uniform random distribution. Basically, if we can factor
// the key this easily, so can anyone else.
func checkPrimeFactorsTooClose(n *big.Int, rounds int) error {
// Pre-allocate some big numbers that we'll use a lot down below.
one := big.NewInt(1)
bb := new(big.Int)
// Any odd integer is equal to a difference of squares of integers:
// n = a^2 - b^2 = (a + b)(a - b)
// Any RSA public key modulus is equal to a product of two primes:
// n = pq
// Here we try to find values for a and b, since doing so also gives us the
// prime factors p = (a + b) and q = (a - b).
// We start with a close to the square root of the modulus n, to start with
// two candidate prime factors that are as close together as possible and
// work our way out from there. Specifically, we set a = ceil(sqrt(n)), the
// first integer greater than the square root of n. Unfortunately, big.Int's
// built-in square root function takes the floor, so we have to add one to get
// the ceil.
a := new(big.Int)
a.Sqrt(n).Add(a, one)
// We calculate b2 to see if it is a perfect square (i.e. b^2), and therefore
// b is an integer. Specifically, b2 = a^2 - n.
b2 := new(big.Int)
b2.Mul(a, a).Sub(b2, n)
for i := 0; i < rounds; i++ {
// To see if b2 is a perfect square, we take its square root, square that,
// and check to see if we got the same result back.
bb.Sqrt(b2).Mul(bb, bb)
if b2.Cmp(bb) == 0 {
// b2 is a perfect square, so we've found integer values of a and b,
// and can easily compute p and q as their sum and difference.
bb.Sqrt(bb)
p := new(big.Int).Add(a, bb)
q := new(big.Int).Sub(a, bb)
return fmt.Errorf("public modulus n = pq factored into p: %s; q: %s", p, q)
}
// Set up the next iteration by incrementing a by one and recalculating b2.
a.Add(a, one)
b2.Mul(a, a).Sub(b2, n)
}
return nil
}
|