mirror of
https://github.com/Telecominfraproject/OpenCellular.git
synced 2026-01-08 16:41:55 +00:00
Split bn_modexp() into three variants: bn_modexp() for large exponents (as before) bn_modexp_word() for single word public exponents bn_modexp_blinded() for large exponents w/ randomization We randomize bn_modexp_blinded() with: 1) pick 64 bit random R1 and compute R1 ** -1 and R1 ** pubexp, mod N. 2) multiply input by R1 ** pubexp 3) pick 64 bit random R2 and add (e*d*R2 - R2) to private exponent (i.e. a random multiple of phi(N)) 4) exponentiate 5) multiply output w/ R1 ** -1 to obtain expected result Since we enlarge the exponent, bn_modexp_blinded() is slower than bn_modexp(). We only use bn_modexp_blinded() when private exponents are in play and we have phi(N) available. Also refactored the combined p256 and rsa dcrypto binary blob into two parts. And added unique first word to each dcrypto blob to make code caching reliable. The TPM task stack maxes out at 8040/8192 in tcg_test due to increased stack usage of bn_modexp_blinded() but is still within safe bounds, with 88 byte redzone. BRANCH=cr50 BUG=b:35587382,b:35587381 TEST=buildall, tcg_test (200+) Change-Id: Ied1f908418f31f8025363179537aa4ebd2c80420 Reviewed-on: https://chromium-review.googlesource.com/540684 Reviewed-by: Marius Schilder <mschilder@chromium.org> Reviewed-by: Vadim Bendebury <vbendeb@chromium.org> Tested-by: Marius Schilder <mschilder@chromium.org>
744 lines
19 KiB
C
744 lines
19 KiB
C
/* Copyright 2015 The Chromium OS Authors. All rights reserved.
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
#include "dcrypto.h"
|
|
#include "internal.h"
|
|
|
|
#include "trng.h"
|
|
#include "util.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "cryptoc/sha.h"
|
|
#include "cryptoc/sha256.h"
|
|
#include "cryptoc/sha384.h"
|
|
#include "cryptoc/sha512.h"
|
|
#include "cryptoc/util.h"
|
|
|
|
/* Extend the MSB throughout the word. */
|
|
static uint32_t msb_extend(uint32_t a)
|
|
{
|
|
return 0u - (a >> 31);
|
|
}
|
|
|
|
/* Return 0xFF..FF if a is zero, and zero otherwise. */
|
|
static uint32_t is_zero(uint32_t a)
|
|
{
|
|
return msb_extend(~a & (a - 1));
|
|
}
|
|
|
|
/* Select a or b based on mask. Mask expected to be 0xFF..FF or 0. */
|
|
static uint32_t select(uint32_t mask, uint32_t a, uint32_t b)
|
|
{
|
|
return (mask & a) | (~mask & b);
|
|
}
|
|
|
|
static void MGF1_xor(uint8_t *dst, uint32_t dst_len,
|
|
const uint8_t *seed, uint32_t seed_len,
|
|
enum hashing_mode hashing)
|
|
{
|
|
HASH_CTX ctx;
|
|
struct {
|
|
uint8_t b3;
|
|
uint8_t b2;
|
|
uint8_t b1;
|
|
uint8_t b0;
|
|
} cnt;
|
|
const uint8_t *digest;
|
|
const size_t hash_size = (hashing == HASH_SHA1) ? SHA_DIGEST_SIZE
|
|
: SHA256_DIGEST_SIZE;
|
|
|
|
cnt.b0 = cnt.b1 = cnt.b2 = cnt.b3 = 0;
|
|
while (dst_len) {
|
|
int i;
|
|
|
|
if (hashing == HASH_SHA1)
|
|
DCRYPTO_SHA1_init(&ctx, 0);
|
|
else
|
|
DCRYPTO_SHA256_init(&ctx, 0);
|
|
|
|
HASH_update(&ctx, seed, seed_len);
|
|
HASH_update(&ctx, (uint8_t *) &cnt, sizeof(cnt));
|
|
digest = HASH_final(&ctx);
|
|
for (i = 0; i < dst_len && i < hash_size; ++i)
|
|
*dst++ ^= *digest++;
|
|
dst_len -= i;
|
|
if (!++cnt.b0)
|
|
++cnt.b1;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* struct OAEP { // MSB to LSB.
|
|
* uint8_t zero;
|
|
* uint8_t seed[HASH_SIZE];
|
|
* uint8_t phash[HASH_SIZE];
|
|
* uint8_t PS[]; // Variable length (optional) zero-pad.
|
|
* uint8_t one; // 0x01, message demarcator.
|
|
* uint8_t msg[]; // Input message.
|
|
* };
|
|
*/
|
|
/* encrypt */
|
|
static int oaep_pad(uint8_t *output, uint32_t output_len,
|
|
const uint8_t *msg, uint32_t msg_len,
|
|
enum hashing_mode hashing, const char *label)
|
|
{
|
|
int i;
|
|
const size_t hash_size = (hashing == HASH_SHA1) ? SHA_DIGEST_SIZE
|
|
: SHA256_DIGEST_SIZE;
|
|
uint8_t *const seed = output + 1;
|
|
uint8_t *const phash = seed + hash_size;
|
|
uint8_t *const PS = phash + hash_size;
|
|
const uint32_t max_msg_len = output_len - 2 - 2 * hash_size;
|
|
const uint32_t ps_len = max_msg_len - msg_len;
|
|
uint8_t *const one = PS + ps_len;
|
|
struct HASH_CTX ctx;
|
|
|
|
if (output_len < 2 + 2 * hash_size)
|
|
return 0; /* Key size too small for chosen hash. */
|
|
if (msg_len > output_len - 2 - 2 * hash_size)
|
|
return 0; /* Input message too large for key size. */
|
|
|
|
always_memset(output, 0, output_len);
|
|
for (i = 0; i < hash_size;) {
|
|
uint32_t r = rand();
|
|
|
|
seed[i++] = r >> 0;
|
|
seed[i++] = r >> 8;
|
|
seed[i++] = r >> 16;
|
|
seed[i++] = r >> 24;
|
|
}
|
|
|
|
if (hashing == HASH_SHA1)
|
|
DCRYPTO_SHA1_init(&ctx, 0);
|
|
else
|
|
DCRYPTO_SHA256_init(&ctx, 0);
|
|
|
|
HASH_update(&ctx, label, label ? strlen(label) + 1 : 0);
|
|
memcpy(phash, HASH_final(&ctx), hash_size);
|
|
*one = 1;
|
|
memcpy(one + 1, msg, msg_len);
|
|
MGF1_xor(phash, hash_size + 1 + max_msg_len,
|
|
seed, hash_size, hashing);
|
|
MGF1_xor(seed, hash_size, phash, hash_size + 1 + max_msg_len,
|
|
hashing);
|
|
return 1;
|
|
}
|
|
|
|
/* decrypt */
|
|
static int check_oaep_pad(uint8_t *out, uint32_t *out_len,
|
|
uint8_t *padded, uint32_t padded_len,
|
|
enum hashing_mode hashing, const char *label)
|
|
{
|
|
const size_t hash_size = (hashing == HASH_SHA1) ? SHA_DIGEST_SIZE
|
|
: SHA256_DIGEST_SIZE;
|
|
uint8_t *seed = padded + 1;
|
|
uint8_t *phash = seed + hash_size;
|
|
uint8_t *PS = phash + hash_size;
|
|
const uint32_t max_msg_len = padded_len - 2 - 2 * hash_size;
|
|
struct HASH_CTX ctx;
|
|
size_t one_index = 0;
|
|
uint32_t looking_for_one_byte = ~0;
|
|
int bad;
|
|
int i;
|
|
|
|
if (padded_len < 2 + 2 * hash_size)
|
|
return 0; /* Invalid input size. */
|
|
|
|
/* Recover seed. */
|
|
MGF1_xor(seed, hash_size, phash, hash_size + 1 + max_msg_len, hashing);
|
|
/* Recover db. */
|
|
MGF1_xor(phash, hash_size + 1 + max_msg_len, seed, hash_size, hashing);
|
|
|
|
if (hashing == HASH_SHA1)
|
|
DCRYPTO_SHA1_init(&ctx, 0);
|
|
else
|
|
DCRYPTO_SHA256_init(&ctx, 0);
|
|
HASH_update(&ctx, label, label ? strlen(label) + 1 : 0);
|
|
|
|
bad = !DCRYPTO_equals(phash, HASH_final(&ctx), hash_size);
|
|
bad |= padded[0];
|
|
|
|
for (i = PS - padded; i < padded_len; i++) {
|
|
uint32_t equals0 = is_zero(padded[i]);
|
|
uint32_t equals1 = is_zero(padded[i] ^ 1);
|
|
|
|
one_index = select(looking_for_one_byte & equals1,
|
|
i, one_index);
|
|
looking_for_one_byte = select(equals1, 0, looking_for_one_byte);
|
|
|
|
/* Bad padding if padded[i] is neither 1 nor 0. */
|
|
bad |= looking_for_one_byte & ~equals0;
|
|
}
|
|
|
|
bad |= looking_for_one_byte;
|
|
|
|
if (bad)
|
|
return 0;
|
|
|
|
one_index++;
|
|
if (*out_len < padded_len - one_index)
|
|
return 0;
|
|
memcpy(out, padded + one_index, padded_len - one_index);
|
|
*out_len = padded_len - one_index;
|
|
return 1;
|
|
}
|
|
|
|
/* Constants from RFC 3447. */
|
|
#define RSA_PKCS1_PADDING_SIZE 11
|
|
|
|
/* encrypt */
|
|
static int pkcs1_type2_pad(uint8_t *padded, uint32_t padded_len,
|
|
const uint8_t *in, uint32_t in_len)
|
|
{
|
|
uint32_t PS_len;
|
|
|
|
if (padded_len < RSA_PKCS1_PADDING_SIZE)
|
|
return 0;
|
|
if (in_len > padded_len - RSA_PKCS1_PADDING_SIZE)
|
|
return 0;
|
|
PS_len = padded_len - 3 - in_len;
|
|
|
|
*(padded++) = 0;
|
|
*(padded++) = 2;
|
|
while (PS_len) {
|
|
int i;
|
|
uint32_t r = rand();
|
|
|
|
for (i = 0; i < 4 && PS_len; i++) {
|
|
uint8_t b = ((uint8_t *) &r)[i];
|
|
|
|
if (b) {
|
|
*padded++ = b;
|
|
PS_len--;
|
|
}
|
|
}
|
|
}
|
|
*(padded++) = 0;
|
|
memcpy(padded, in, in_len);
|
|
return 1;
|
|
}
|
|
|
|
/* decrypt */
|
|
static int check_pkcs1_type2_pad(uint8_t *out, uint32_t *out_len,
|
|
const uint8_t *padded, uint32_t padded_len)
|
|
{
|
|
int i;
|
|
int valid;
|
|
uint32_t zero_index = 0;
|
|
uint32_t looking_for_index = ~0;
|
|
|
|
if (padded_len < RSA_PKCS1_PADDING_SIZE)
|
|
return 0;
|
|
|
|
valid = (padded[0] == 0);
|
|
valid &= (padded[1] == 2);
|
|
|
|
for (i = 2; i < padded_len; i++) {
|
|
uint32_t found = is_zero(padded[i]);
|
|
|
|
zero_index = select(looking_for_index & found, i, zero_index);
|
|
looking_for_index = select(found, 0, looking_for_index);
|
|
}
|
|
|
|
zero_index++;
|
|
|
|
valid &= ~looking_for_index;
|
|
valid &= (zero_index >= RSA_PKCS1_PADDING_SIZE);
|
|
if (!valid)
|
|
return 0;
|
|
|
|
if (*out_len < padded_len - zero_index)
|
|
return 0;
|
|
memcpy(out, &padded[zero_index], padded_len - zero_index);
|
|
*out_len = padded_len - zero_index;
|
|
return 1;
|
|
}
|
|
|
|
static const uint8_t SHA1_DER[] = {
|
|
0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e,
|
|
0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14
|
|
};
|
|
static const uint8_t SHA256_DER[] = {
|
|
0x30, 0x31, 0x30, 0x0D, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
|
|
0x00, 0x04, 0x20
|
|
};
|
|
static const uint8_t SHA384_DER[] = {
|
|
0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05,
|
|
0x00, 0x04, 0x30
|
|
};
|
|
static const uint8_t SHA512_DER[] = {
|
|
0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
|
|
0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05,
|
|
0x00, 0x04, 0x40
|
|
};
|
|
|
|
static int pkcs1_get_der(enum hashing_mode hashing, const uint8_t **der,
|
|
uint32_t *der_size, uint32_t *hash_size)
|
|
{
|
|
switch (hashing) {
|
|
case HASH_SHA1:
|
|
*der = &SHA1_DER[0];
|
|
*der_size = sizeof(SHA1_DER);
|
|
*hash_size = SHA_DIGEST_SIZE;
|
|
break;
|
|
case HASH_SHA256:
|
|
*der = &SHA256_DER[0];
|
|
*der_size = sizeof(SHA256_DER);
|
|
*hash_size = SHA256_DIGEST_SIZE;
|
|
break;
|
|
case HASH_SHA384:
|
|
*der = &SHA384_DER[0];
|
|
*der_size = sizeof(SHA384_DER);
|
|
*hash_size = SHA384_DIGEST_SIZE;
|
|
break;
|
|
case HASH_SHA512:
|
|
*der = &SHA512_DER[0];
|
|
*der_size = sizeof(SHA512_DER);
|
|
*hash_size = SHA512_DIGEST_SIZE;
|
|
break;
|
|
case HASH_NULL:
|
|
*der = NULL;
|
|
*der_size = 0;
|
|
*hash_size = 0; /* any size allowed */
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* sign */
|
|
static int pkcs1_type1_pad(uint8_t *padded, uint32_t padded_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
enum hashing_mode hashing)
|
|
{
|
|
const uint8_t *der;
|
|
uint32_t der_size;
|
|
uint32_t hash_size;
|
|
uint32_t ps_len;
|
|
|
|
if (!pkcs1_get_der(hashing, &der, &der_size, &hash_size))
|
|
return 0;
|
|
if (padded_len < RSA_PKCS1_PADDING_SIZE + der_size)
|
|
return 0;
|
|
if (!in_len || (hash_size && in_len != hash_size))
|
|
return 0;
|
|
if (in_len > padded_len - RSA_PKCS1_PADDING_SIZE - der_size)
|
|
return 0;
|
|
ps_len = padded_len - 3 - der_size - in_len;
|
|
|
|
*(padded++) = 0;
|
|
*(padded++) = 1;
|
|
always_memset(padded, 0xFF, ps_len);
|
|
padded += ps_len;
|
|
*(padded++) = 0;
|
|
memcpy(padded, der, der_size);
|
|
padded += der_size;
|
|
memcpy(padded, in, in_len);
|
|
return 1;
|
|
}
|
|
|
|
/* verify */
|
|
static int check_pkcs1_type1_pad(const uint8_t *msg, uint32_t msg_len,
|
|
const uint8_t *padded, uint32_t padded_len,
|
|
enum hashing_mode hashing)
|
|
{
|
|
int i;
|
|
const uint8_t *der;
|
|
uint32_t der_size;
|
|
uint32_t hash_size;
|
|
uint32_t ps_len;
|
|
|
|
if (!pkcs1_get_der(hashing, &der, &der_size, &hash_size))
|
|
return 0;
|
|
if (msg_len != hash_size)
|
|
return 0;
|
|
if (padded_len < RSA_PKCS1_PADDING_SIZE + der_size + hash_size)
|
|
return 0;
|
|
ps_len = padded_len - 3 - der_size - hash_size;
|
|
|
|
if (padded[0] != 0 || padded[1] != 1)
|
|
return 0;
|
|
for (i = 2; i < ps_len + 2; i++) {
|
|
if (padded[i] != 0xFF)
|
|
return 0;
|
|
}
|
|
|
|
if (padded[i++] != 0)
|
|
return 0;
|
|
if (!DCRYPTO_equals(&padded[i], der, der_size))
|
|
return 0;
|
|
i += der_size;
|
|
return DCRYPTO_equals(msg, &padded[i], hash_size);
|
|
}
|
|
|
|
/* sign */
|
|
static int pkcs1_pss_pad(uint8_t *padded, uint32_t padded_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
enum hashing_mode hashing)
|
|
{
|
|
const uint32_t hash_size = (hashing == HASH_SHA1) ? SHA_DIGEST_SIZE
|
|
: SHA256_DIGEST_SIZE;
|
|
const uint32_t salt_len = MIN(padded_len - hash_size - 2, hash_size);
|
|
uint32_t db_len;
|
|
uint32_t ps_len;
|
|
struct HASH_CTX ctx;
|
|
|
|
if (in_len != hash_size)
|
|
return 0;
|
|
if (padded_len < hash_size + 2)
|
|
return 0;
|
|
db_len = padded_len - hash_size - 1;
|
|
|
|
if (hashing == HASH_SHA1)
|
|
DCRYPTO_SHA1_init(&ctx, 0);
|
|
else
|
|
DCRYPTO_SHA256_init(&ctx, 0);
|
|
|
|
/* Pilfer bits of output for temporary use. */
|
|
memset(padded, 0, 8);
|
|
HASH_update(&ctx, padded, 8);
|
|
HASH_update(&ctx, in, in_len);
|
|
/* Pilfer bits of output for temporary use. */
|
|
rand_bytes(padded, salt_len);
|
|
HASH_update(&ctx, padded, salt_len);
|
|
|
|
/* Output hash. */
|
|
memcpy(padded + db_len, HASH_final(&ctx), hash_size);
|
|
|
|
/* Prepare DB. */
|
|
ps_len = db_len - salt_len - 1;
|
|
memmove(padded + ps_len + 1, padded, salt_len);
|
|
memset(padded, 0, ps_len);
|
|
padded[ps_len] = 0x01;
|
|
MGF1_xor(padded, db_len, padded + db_len, hash_size, hashing);
|
|
|
|
/* Clear most significant bit. */
|
|
padded[0] &= 0x7F;
|
|
/* Set trailing byte. */
|
|
padded[padded_len - 1] = 0xBC;
|
|
return 1;
|
|
}
|
|
|
|
/* verify */
|
|
static int check_pkcs1_pss_pad(const uint8_t *in, uint32_t in_len,
|
|
uint8_t *padded, uint32_t padded_len,
|
|
enum hashing_mode hashing)
|
|
{
|
|
const uint32_t hash_size = (hashing == HASH_SHA1) ? SHA_DIGEST_SIZE
|
|
: SHA256_DIGEST_SIZE;
|
|
const uint8_t zeros[8] = {0, 0, 0, 0, 0, 0, 0, 0};
|
|
uint32_t db_len;
|
|
uint32_t max_ps_len;
|
|
uint32_t salt_len;
|
|
HASH_CTX ctx;
|
|
int bad = 0;
|
|
int i;
|
|
|
|
if (in_len != hash_size)
|
|
return 0;
|
|
if (padded_len < hash_size + 2)
|
|
return 0;
|
|
db_len = padded_len - hash_size - 1;
|
|
|
|
/* Top bit should be zero. */
|
|
bad |= padded[0] & 0x80;
|
|
/* Check trailing byte. */
|
|
bad |= padded[padded_len - 1] ^ 0xBC;
|
|
|
|
/* Recover DB. */
|
|
MGF1_xor(padded, db_len, padded + db_len, hash_size, hashing);
|
|
/* Clear top bit. */
|
|
padded[0] &= 0x7F;
|
|
/* Verify padding2. */
|
|
max_ps_len = db_len - 1;
|
|
for (i = 0; i < max_ps_len; i++) {
|
|
if (padded[i] == 0x01)
|
|
break;
|
|
else
|
|
bad |= padded[i];
|
|
}
|
|
bad |= (padded[i] ^ 0x01);
|
|
/* Continue with zero-length salt if 0x01 was not found. */
|
|
salt_len = max_ps_len - i;
|
|
|
|
if (hashing == HASH_SHA1)
|
|
DCRYPTO_SHA1_init(&ctx, 0);
|
|
else
|
|
DCRYPTO_SHA256_init(&ctx, 0);
|
|
HASH_update(&ctx, zeros, sizeof(zeros));
|
|
HASH_update(&ctx, in, in_len);
|
|
HASH_update(&ctx, padded + db_len - salt_len, salt_len);
|
|
bad |= !DCRYPTO_equals(padded + db_len, HASH_final(&ctx), hash_size);
|
|
return !bad;
|
|
}
|
|
|
|
static int check_modulus_params(
|
|
const struct LITE_BIGNUM *N, size_t rsa_max_bytes, uint32_t *out_len)
|
|
{
|
|
if (bn_size(N) > rsa_max_bytes)
|
|
return 0; /* Unsupported key size. */
|
|
if (!bn_check_topbit(N)) /* Check that top bit is set. */
|
|
return 0;
|
|
if (out_len && *out_len < bn_size(N))
|
|
return 0; /* Output buffer too small. */
|
|
return 1;
|
|
}
|
|
|
|
int DCRYPTO_rsa_encrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
|
|
const uint8_t *in, uint32_t in_len,
|
|
enum padding_mode padding, enum hashing_mode hashing,
|
|
const char *label)
|
|
{
|
|
uint8_t *p;
|
|
uint32_t padded_buf[RSA_MAX_WORDS];
|
|
uint32_t e_buf[LITE_BN_BYTES / sizeof(uint32_t)];
|
|
|
|
struct LITE_BIGNUM padded;
|
|
struct LITE_BIGNUM encrypted;
|
|
int ret;
|
|
|
|
if (!check_modulus_params(&rsa->N, sizeof(padded_buf), out_len))
|
|
return 0;
|
|
|
|
bn_init(&padded, padded_buf, bn_size(&rsa->N));
|
|
bn_init(&encrypted, out, bn_size(&rsa->N));
|
|
|
|
switch (padding) {
|
|
case PADDING_MODE_OAEP:
|
|
if (!oaep_pad((uint8_t *) padded.d, bn_size(&padded),
|
|
(const uint8_t *) in, in_len, hashing, label))
|
|
return 0;
|
|
break;
|
|
case PADDING_MODE_PKCS1:
|
|
if (!pkcs1_type2_pad((uint8_t *) padded.d, bn_size(&padded),
|
|
(const uint8_t *) in, in_len))
|
|
return 0;
|
|
break;
|
|
case PADDING_MODE_NULL:
|
|
/* Input is allowed to have more bytes than N, in
|
|
* which case the excess must be zero. */
|
|
for (; in_len > bn_size(&padded); in_len--)
|
|
if (*in++ != 0)
|
|
return 0;
|
|
p = (uint8_t *) padded.d;
|
|
/* If in_len < bn_size(&padded), padded will
|
|
* have leading zero bytes. */
|
|
memcpy(&p[bn_size(&padded) - in_len], in, in_len);
|
|
/* TODO(ngm): in may be > N, bn_mod_exp() should
|
|
* handle this case. */
|
|
break;
|
|
default:
|
|
return 0; /* Unsupported padding mode. */
|
|
}
|
|
|
|
/* Reverse from big-endian to little-endian notation. */
|
|
reverse((uint8_t *) padded.d, bn_size(&padded));
|
|
ret = bn_modexp_word(&encrypted, &padded, rsa->e, &rsa->N);
|
|
/* Back to big-endian notation. */
|
|
reverse((uint8_t *) encrypted.d, bn_size(&encrypted));
|
|
*out_len = bn_size(&encrypted);
|
|
|
|
always_memset(padded_buf, 0, sizeof(padded_buf));
|
|
always_memset(e_buf, 0, sizeof(e_buf));
|
|
return ret;
|
|
}
|
|
|
|
int DCRYPTO_rsa_decrypt(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
|
|
const uint8_t *in, const uint32_t in_len,
|
|
enum padding_mode padding, enum hashing_mode hashing,
|
|
const char *label)
|
|
{
|
|
uint32_t encrypted_buf[RSA_MAX_WORDS];
|
|
uint32_t padded_buf[RSA_MAX_WORDS];
|
|
|
|
struct LITE_BIGNUM encrypted;
|
|
struct LITE_BIGNUM padded;
|
|
int ret;
|
|
|
|
if (!check_modulus_params(&rsa->N, sizeof(padded_buf), NULL))
|
|
return 0;
|
|
if (in_len != bn_size(&rsa->N))
|
|
return 0; /* Invalid input length. */
|
|
|
|
/* TODO(ngm): this copy can be eliminated if input may be modified. */
|
|
bn_init(&encrypted, encrypted_buf, in_len);
|
|
memcpy(encrypted_buf, in, in_len);
|
|
bn_init(&padded, padded_buf, in_len);
|
|
|
|
/* Reverse from big-endian to little-endian notation. */
|
|
reverse((uint8_t *) encrypted.d, encrypted.dmax * LITE_BN_BYTES);
|
|
ret = bn_modexp_blinded(&padded, &encrypted, &rsa->d, &rsa->N, rsa->e);
|
|
/* Back to big-endian notation. */
|
|
reverse((uint8_t *) padded.d, padded.dmax * LITE_BN_BYTES);
|
|
|
|
switch (padding) {
|
|
case PADDING_MODE_OAEP:
|
|
if (!check_oaep_pad(out, out_len, (uint8_t *) padded.d,
|
|
bn_size(&padded), hashing, label))
|
|
ret = 0;
|
|
break;
|
|
case PADDING_MODE_PKCS1:
|
|
if (!check_pkcs1_type2_pad(
|
|
out, out_len, (const uint8_t *) padded.d,
|
|
bn_size(&padded)))
|
|
ret = 0;
|
|
break;
|
|
case PADDING_MODE_NULL:
|
|
if (*out_len < bn_size(&padded)) {
|
|
ret = 0;
|
|
} else {
|
|
*out_len = bn_size(&padded);
|
|
memcpy(out, padded.d, *out_len);
|
|
}
|
|
break;
|
|
default:
|
|
/* Unsupported padding mode. */
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
always_memset(encrypted_buf, 0, sizeof(encrypted_buf));
|
|
always_memset(padded_buf, 0, sizeof(padded_buf));
|
|
return ret;
|
|
}
|
|
|
|
int DCRYPTO_rsa_sign(struct RSA *rsa, uint8_t *out, uint32_t *out_len,
|
|
const uint8_t *in, const uint32_t in_len,
|
|
enum padding_mode padding, enum hashing_mode hashing)
|
|
{
|
|
uint32_t padded_buf[RSA_MAX_WORDS];
|
|
|
|
struct LITE_BIGNUM padded;
|
|
struct LITE_BIGNUM signature;
|
|
int ret;
|
|
|
|
if (!check_modulus_params(&rsa->N, sizeof(padded_buf), out_len))
|
|
return 0;
|
|
|
|
bn_init(&padded, padded_buf, bn_size(&rsa->N));
|
|
bn_init(&signature, out, bn_size(&rsa->N));
|
|
|
|
switch (padding) {
|
|
case PADDING_MODE_PKCS1:
|
|
if (!pkcs1_type1_pad((uint8_t *) padded.d, bn_size(&padded),
|
|
(const uint8_t *) in, in_len, hashing))
|
|
return 0;
|
|
break;
|
|
case PADDING_MODE_PSS:
|
|
if (!pkcs1_pss_pad((uint8_t *) padded.d, bn_size(&padded),
|
|
(const uint8_t *) in, in_len, hashing))
|
|
return 0;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
/* Reverse from big-endian to little-endian notation. */
|
|
reverse((uint8_t *) padded.d, bn_size(&padded));
|
|
ret = bn_modexp_blinded(&signature, &padded, &rsa->d, &rsa->N, rsa->e);
|
|
/* Back to big-endian notation. */
|
|
reverse((uint8_t *) signature.d, bn_size(&signature));
|
|
*out_len = bn_size(&rsa->N);
|
|
|
|
always_memset(padded_buf, 0, sizeof(padded_buf));
|
|
return ret;
|
|
}
|
|
|
|
int DCRYPTO_rsa_verify(const struct RSA *rsa, const uint8_t *digest,
|
|
uint32_t digest_len, const uint8_t *sig,
|
|
const uint32_t sig_len, enum padding_mode padding,
|
|
enum hashing_mode hashing)
|
|
{
|
|
uint32_t padded_buf[RSA_WORDS_4K];
|
|
uint32_t signature_buf[RSA_WORDS_4K];
|
|
|
|
struct LITE_BIGNUM padded;
|
|
struct LITE_BIGNUM signature;
|
|
int ret;
|
|
|
|
if (!check_modulus_params(&rsa->N, sizeof(padded_buf), NULL))
|
|
return 0;
|
|
if (sig_len != bn_size(&rsa->N))
|
|
return 0; /* Invalid input length. */
|
|
|
|
bn_init(&signature, signature_buf, bn_size(&rsa->N));
|
|
memcpy(signature_buf, sig, bn_size(&rsa->N));
|
|
bn_init(&padded, padded_buf, bn_size(&rsa->N));
|
|
|
|
/* Reverse from big-endian to little-endian notation. */
|
|
reverse((uint8_t *) signature.d, bn_size(&signature));
|
|
ret = bn_modexp_word(&padded, &signature, rsa->e, &rsa->N);
|
|
/* Back to big-endian notation. */
|
|
reverse((uint8_t *) padded.d, bn_size(&padded));
|
|
|
|
switch (padding) {
|
|
case PADDING_MODE_PKCS1:
|
|
if (!check_pkcs1_type1_pad(
|
|
digest, digest_len, (uint8_t *) padded.d,
|
|
bn_size(&padded), hashing))
|
|
ret = 0;
|
|
break;
|
|
case PADDING_MODE_PSS:
|
|
if (!check_pkcs1_pss_pad(
|
|
digest, digest_len, (uint8_t *) padded.d,
|
|
bn_size(&padded), hashing))
|
|
ret = 0;
|
|
break;
|
|
default:
|
|
/* Unsupported padding mode. */
|
|
ret = 0;
|
|
break;
|
|
}
|
|
|
|
always_memset(padded_buf, 0, sizeof(padded_buf));
|
|
always_memset(signature_buf, 0, sizeof(signature_buf));
|
|
return ret;
|
|
}
|
|
|
|
int DCRYPTO_rsa_key_compute(struct LITE_BIGNUM *N, struct LITE_BIGNUM *d,
|
|
struct LITE_BIGNUM *p, struct LITE_BIGNUM *q,
|
|
uint32_t e_buf)
|
|
{
|
|
uint32_t ONE_buf = 1;
|
|
uint32_t phi_buf[RSA_MAX_WORDS];
|
|
uint32_t q_buf[RSA_MAX_WORDS / 2 + 1];
|
|
|
|
struct LITE_BIGNUM ONE;
|
|
struct LITE_BIGNUM e;
|
|
struct LITE_BIGNUM phi;
|
|
struct LITE_BIGNUM q_local;
|
|
|
|
DCRYPTO_bn_wrap(&ONE, &ONE_buf, sizeof(ONE_buf));
|
|
DCRYPTO_bn_wrap(&phi, phi_buf, bn_size(N));
|
|
if (!q) {
|
|
/* q not provided, calculate it. */
|
|
memcpy(phi_buf, N->d, bn_size(N));
|
|
bn_init(&q_local, q_buf, bn_size(p));
|
|
q = &q_local;
|
|
|
|
if (!DCRYPTO_bn_div(q, NULL, &phi, p))
|
|
return 0;
|
|
|
|
/* Check that p * q == N */
|
|
DCRYPTO_bn_mul(&phi, p, q);
|
|
if (!bn_eq(N, &phi))
|
|
return 0;
|
|
} else {
|
|
DCRYPTO_bn_mul(N, p, q);
|
|
memcpy(phi_buf, N->d, bn_size(N));
|
|
}
|
|
|
|
bn_sub(&phi, p);
|
|
bn_sub(&phi, q);
|
|
bn_add(&phi, &ONE);
|
|
DCRYPTO_bn_wrap(&e, &e_buf, sizeof(e_buf));
|
|
return bn_modinv_vartime(d, &e, &phi);
|
|
}
|