blob: 694fd0a918eed993ade8563f48b7981ead40d232 [file] [log] [blame] [edit]
/*
* Copyright (c) 2014, Google Inc.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
/*
* This file is a modified version of rsa-sign.c found in
* U-Boot (http://www.denx.de/wiki/U-Boot)
*/
#include "rsa_params.h"
#include <openssl/err.h>
#include <openssl/ssl.h>
#if OPENSSL_VERSION_NUMBER >= 0x10000000L
#define HAVE_ERR_REMOVE_THREAD_STATE
#endif
static int rsa_err(const char *msg)
{
unsigned long sslErr = ERR_get_error();
fprintf(stderr, "%s", msg);
fprintf(stderr, ": %s\n",
ERR_error_string(sslErr, 0));
return -1;
}
/**
* rsa_get_pub_key() - read a public key from a .pub file
*
* @path Path to .pub file
* @rsap Returns RSA object, or NULL on failure
* @return 0 if ok, -ve on error (in which case *rsap will be set to NULL)
*/
static int rsa_get_pub_key(char *path, RSA **rsap)
{
RSA *rsa = NULL;
FILE *f;
int ret;
*rsap = NULL;
f = fopen(path, "r");
if (!f) {
fprintf(stderr, "Couldn't open RSA pub: '%s': %s\n", path, strerror(errno));
return -EACCES;
}
if (!PEM_read_RSA_PUBKEY(f, &rsa, NULL, NULL)) {
rsa_err("Couldn't read pub file");
ret = -EINVAL;
goto err_rsa;
}
fclose(f);
*rsap = rsa;
return 0;
err_rsa:
fclose(f);
return ret;
}
static int rsa_init(void)
{
int ret;
ret = SSL_library_init();
if (!ret) {
fprintf(stderr, "Failure to init SSL library\n");
return -1;
}
SSL_load_error_strings();
OpenSSL_add_all_algorithms();
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
return 0;
}
static void rsa_remove(void)
{
CRYPTO_cleanup_all_ex_data();
ERR_free_strings();
#ifdef HAVE_ERR_REMOVE_THREAD_STATE
ERR_remove_thread_state(NULL);
#else
ERR_remove_state(0);
#endif
EVP_cleanup();
}
/*
* rsa_get_params(): - Get the important parameters of an RSA public key
*/
int rsa_get_params(RSA *key, uint32_t *n0_invp, BIGNUM **modulusp,
BIGNUM **r_squaredp)
{
BIGNUM *big1, *big2, *big32, *big2_32;
BIGNUM *n, *r, *r_squared, *tmp;
BN_CTX *bn_ctx = BN_CTX_new();
int ret = 0;
/* Initialize BIGNUMs */
big1 = BN_new();
big2 = BN_new();
big32 = BN_new();
r = BN_new();
r_squared = BN_new();
tmp = BN_new();
big2_32 = BN_new();
n = BN_new();
if (!big1 || !big2 || !big32 || !r || !r_squared || !tmp || !big2_32 ||
!n) {
fprintf(stderr, "Out of memory (bignum)\n");
return -ENOMEM;
}
if (!BN_copy(n, key->n) || !BN_set_word(big1, 1L) ||
!BN_set_word(big2, 2L) || !BN_set_word(big32, 32L))
ret = -1;
/* big2_32 = 2^32 */
if (!BN_exp(big2_32, big2, big32, bn_ctx))
ret = -1;
/* Calculate n0_inv = -1 / n[0] mod 2^32 */
if (!BN_mod_inverse(tmp, n, big2_32, bn_ctx) ||
!BN_sub(tmp, big2_32, tmp))
ret = -1;
*n0_invp = BN_get_word(tmp);
/* Calculate R = 2^(# of key bits) */
if (!BN_set_word(tmp, BN_num_bits(n)) ||
!BN_exp(r, big2, tmp, bn_ctx))
ret = -1;
/* Calculate r_squared = R^2 mod n */
if (!BN_copy(r_squared, r) ||
!BN_mul(tmp, r_squared, r, bn_ctx) ||
!BN_mod(r_squared, tmp, n, bn_ctx))
ret = -1;
*modulusp = n;
*r_squaredp = r_squared;
BN_free(big1);
BN_free(big2);
BN_free(big32);
BN_free(r);
BN_free(tmp);
BN_free(big2_32);
if (ret) {
fprintf(stderr, "Bignum operations failed\n");
return -ENOMEM;
}
return ret;
}
/*
* Convert a BIGNUM to a little endian array of uint32_ts.
*
* The caller is responsible for free-ing *arr after use.
*/
static int bignum_to_uint32_t(BIGNUM *num, int num_bits, uint32_t **arr)
{
int nwords = num_bits / 32;
int size;
uint32_t *ptr;
BIGNUM *tmp, *big2, *big32, *big2_32;
BN_CTX *ctx;
tmp = BN_new();
big2 = BN_new();
big32 = BN_new();
big2_32 = BN_new();
if (!tmp || !big2 || !big32 || !big2_32) {
fprintf(stderr, "Out of memory (bignum)\n");
return -ENOMEM;
}
ctx = BN_CTX_new();
if (!tmp) {
fprintf(stderr, "Out of memory (bignum context)\n");
return -ENOMEM;
}
BN_set_word(big2, 2L);
BN_set_word(big32, 32L);
BN_exp(big2_32, big2, big32, ctx); /* B = 2^32 */
size = nwords * sizeof(uint32_t);
*arr = malloc(size);
if (!(*arr)) {
fprintf(stderr, "Out of memory (%d bytes)\n", size);
return -ENOMEM;
}
/* Write out bignum as little endian array of integers */
for (ptr = *arr; ptr < *arr + nwords; ptr++) {
BN_mod(tmp, num, big2_32, ctx); /* n = N mod B */
*ptr = BN_get_word(tmp);
BN_rshift(num, num, 32); /* N = N/B */
}
BN_free(tmp);
BN_free(big2);
BN_free(big32);
BN_free(big2_32);
return 0;
}
int rsa_get_verify_data(char *filepath, uint32_t **modulus_ptr,
uint32_t **r_squared_ptr, uint32_t *n0_inv, int *bits)
{
BIGNUM *modulus, *r_squared;
int ret;
RSA *rsa;
ret = rsa_init();
if (ret)
return ret;
ret = rsa_get_pub_key(filepath, &rsa);
if (ret)
return ret;
ret = rsa_get_params(rsa, n0_inv, &modulus, &r_squared);
if (ret)
return ret;
*bits = BN_num_bits(modulus);
bignum_to_uint32_t(modulus, *bits, modulus_ptr);
bignum_to_uint32_t(r_squared, *bits, r_squared_ptr);
BN_free(modulus);
BN_free(r_squared);
rsa_remove();
if (ret)
return -EIO;
return 0;
}