RSA security bits calculation
NIST has updated their guidelines in appendix D of SP 800-56B rev2 (draft) providing a formula for the number of security bits it terms of the length of the RSA key. This is an implementation of this formula using fixed point arithmetic. For integers 1 .. 100,000 it rounds down to the next smaller 8 bit strength 270 times. It never errs to the high side. None of the rounded values occur near any of the commonly selected lengths. Reviewed-by: Matt Caswell <matt@openssl.org> (Merged from https://github.com/openssl/openssl/pull/7352)
This commit is contained in:
parent
5b4cb385c1
commit
97b0b713fb
2 changed files with 188 additions and 1 deletions
|
@ -163,6 +163,133 @@ void *RSA_get_ex_data(const RSA *r, int idx)
|
|||
return CRYPTO_get_ex_data(&r->ex_data, idx);
|
||||
}
|
||||
|
||||
/*
|
||||
* Define a scaling constant for our fixed point arithmetic.
|
||||
* This value must be a power of two because the base two logarithm code
|
||||
* makes this assumption. The exponent must also be a multiple of three so
|
||||
* that the scale factor has an exact cube root. Finally, the scale factor
|
||||
* should not be so large that a multiplication of two scaled numbers
|
||||
* overflows a 64 bit unsigned integer.
|
||||
*/
|
||||
static const unsigned int scale = 1 << 18;
|
||||
static const unsigned int cbrt_scale = 1 << (2 * 18 / 3);
|
||||
|
||||
/* Define some constants, none exceed 32 bits */
|
||||
static const unsigned int log_2 = 0x02c5c8; /* scale * log(2) */
|
||||
static const unsigned int log_e = 0x05c551; /* scale * log2(M_E) */
|
||||
static const unsigned int c1_923 = 0x07b126; /* scale * 1.923 */
|
||||
static const unsigned int c4_690 = 0x12c28f; /* scale * 4.690 */
|
||||
|
||||
/*
|
||||
* Multiply two scale integers together and rescale the result.
|
||||
*/
|
||||
static ossl_inline uint64_t mul2(uint64_t a, uint64_t b)
|
||||
{
|
||||
return a * b / scale;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the cube root of a 64 bit scaled integer.
|
||||
* Although the cube root of a 64 bit number does fit into a 32 bit unsigned
|
||||
* integer, this is not guaranteed after scaling, so this function has a
|
||||
* 64 bit return. This uses the shifting nth root algorithm with some
|
||||
* algebraic simplifications.
|
||||
*/
|
||||
static uint64_t icbrt64(uint64_t x)
|
||||
{
|
||||
uint64_t r = 0;
|
||||
uint64_t b;
|
||||
int s;
|
||||
|
||||
for (s = 63; s >= 0; s -= 3) {
|
||||
r <<= 1;
|
||||
b = 3 * r * (r + 1) + 1;
|
||||
if ((x >> s) >= b) {
|
||||
x -= b << s;
|
||||
r++;
|
||||
}
|
||||
}
|
||||
return r * cbrt_scale;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the natural logarithm of a 64 bit scaled integer.
|
||||
* This is done by calculating a base two logarithm and scaling.
|
||||
* The maximum logarithm (base 2) is 64 and this reduces base e, so
|
||||
* a 32 bit result should not overflow. The argument passed must be
|
||||
* greater than unity so we don't need to handle negative results.
|
||||
*/
|
||||
static uint32_t ilog_e(uint64_t v)
|
||||
{
|
||||
uint32_t i, r = 0;
|
||||
|
||||
/*
|
||||
* Scale down the value into the range 1 .. 2.
|
||||
*
|
||||
* If fractional numbers need to be processed, another loop needs
|
||||
* to go here that checks v < scale and if so multiplies it by 2 and
|
||||
* reduces r by scale. This also means making r signed.
|
||||
*/
|
||||
while (v >= 2 * scale) {
|
||||
v >>= 1;
|
||||
r += scale;
|
||||
}
|
||||
for (i = scale / 2; i != 0; i /= 2) {
|
||||
v = mul2(v, v);
|
||||
if (v >= 2 * scale) {
|
||||
v >>= 1;
|
||||
r += i;
|
||||
}
|
||||
}
|
||||
r = (r * (uint64_t)scale) / log_e;
|
||||
return r;
|
||||
}
|
||||
|
||||
/*
|
||||
* NIST SP 800-56B rev 2 Appendix D: Maximum Security Strength Estimates for IFC
|
||||
* Modulus Lengths.
|
||||
*
|
||||
* E = \frac{1.923 \sqrt[3]{nBits \cdot log_e(2)}
|
||||
* \cdot(log_e(nBits \cdot log_e(2))^{2/3} - 4.69}{log_e(2)}
|
||||
* The two cube roots are merged together here.
|
||||
*/
|
||||
static uint16_t rsa_compute_security_bits(int n)
|
||||
{
|
||||
uint64_t x;
|
||||
uint32_t lx;
|
||||
uint16_t y;
|
||||
|
||||
/* Look for common values as listed in SP 800-56B rev 2 Appendix D */
|
||||
switch (n) {
|
||||
case 2048:
|
||||
return 112;
|
||||
case 3072:
|
||||
return 128;
|
||||
case 4096:
|
||||
return 152;
|
||||
case 6144:
|
||||
return 176;
|
||||
case 8192:
|
||||
return 200;
|
||||
}
|
||||
/*
|
||||
* The first incorrect result (i.e. not accurate or off by one low) occurs
|
||||
* for n = 699668. The true value here is 1200. Instead of using this n
|
||||
* as the check threshold, the smallest n such that the correct result is
|
||||
* 1200 is used instead.
|
||||
*/
|
||||
if (n >= 687737)
|
||||
return 1200;
|
||||
if (n < 8)
|
||||
return 0;
|
||||
|
||||
x = n * (uint64_t)log_2;
|
||||
lx = ilog_e(x);
|
||||
y = (uint16_t)((mul2(c1_923, icbrt64(mul2(mul2(x, lx), lx))) - c4_690)
|
||||
/ log_2);
|
||||
return (y + 4) & ~7;
|
||||
}
|
||||
|
||||
int RSA_security_bits(const RSA *rsa)
|
||||
{
|
||||
int bits = BN_num_bits(rsa->n);
|
||||
|
@ -174,7 +301,7 @@ int RSA_security_bits(const RSA *rsa)
|
|||
if (ex_primes <= 0 || (ex_primes + 2) > rsa_multip_cap(bits))
|
||||
return 0;
|
||||
}
|
||||
return BN_security_bits(bits, -1);
|
||||
return rsa_compute_security_bits(bits);
|
||||
}
|
||||
|
||||
int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d)
|
||||
|
|
|
@ -329,10 +329,70 @@ err:
|
|||
return ret;
|
||||
}
|
||||
|
||||
static const struct {
|
||||
int bits;
|
||||
unsigned int r;
|
||||
} rsa_security_bits_cases[] = {
|
||||
/* NIST SP 800-56B rev 2 (draft) Appendix D Table 5 */
|
||||
{ 2048, 112 },
|
||||
{ 3072, 128 },
|
||||
{ 4096, 152 },
|
||||
{ 6144, 176 },
|
||||
{ 8192, 200 },
|
||||
/* Older values */
|
||||
{ 256, 40 },
|
||||
{ 512, 56 },
|
||||
{ 1024, 80 },
|
||||
/* Slightly different value to the 256 that NIST lists in their tables */
|
||||
{ 15360, 264 },
|
||||
/* Some other values */
|
||||
{ 8888, 208 },
|
||||
{ 2468, 120 },
|
||||
{ 13456, 248 }
|
||||
};
|
||||
|
||||
static int test_rsa_security_bit(int n)
|
||||
{
|
||||
static const unsigned char vals[8] = {
|
||||
0x80, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40
|
||||
};
|
||||
RSA *key = RSA_new();
|
||||
const int bits = rsa_security_bits_cases[n].bits;
|
||||
const int result = rsa_security_bits_cases[n].r;
|
||||
const int bytes = (bits + 7) / 8;
|
||||
int r = 0;
|
||||
unsigned char num[2000];
|
||||
|
||||
if (!TEST_ptr(key) || !TEST_int_le(bytes, (int)sizeof(num)))
|
||||
goto err;
|
||||
|
||||
/*
|
||||
* It is necessary to set the RSA key in order to ask for the strength.
|
||||
* A BN of an appropriate size is created, in general it won't have the
|
||||
* properties necessary for RSA to function. This is okay here since
|
||||
* the RSA key is never used.
|
||||
*/
|
||||
memset(num, vals[bits % 8], bytes);
|
||||
|
||||
/*
|
||||
* The 'e' parameter is set to the same value as 'n'. This saves having
|
||||
* an extra BN to hold a sensible value for 'e'. This is safe since the
|
||||
* RSA key is not used. The 'd' parameter can be NULL safely.
|
||||
*/
|
||||
if (TEST_true(RSA_set0_key(key, BN_bin2bn(num, bytes, NULL),
|
||||
BN_bin2bn(num, bytes, NULL), NULL))
|
||||
&& TEST_uint_eq(RSA_security_bits(key), result))
|
||||
r = 1;
|
||||
err:
|
||||
RSA_free(key);
|
||||
return r;
|
||||
}
|
||||
|
||||
int setup_tests(void)
|
||||
{
|
||||
ADD_ALL_TESTS(test_rsa_pkcs1, 3);
|
||||
ADD_ALL_TESTS(test_rsa_oaep, 3);
|
||||
ADD_ALL_TESTS(test_rsa_security_bit, OSSL_NELEM(rsa_security_bits_cases));
|
||||
return 1;
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue