/* 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 #include #include #include #include #include #include #include #include extern bool FLAGS_verbose; #define VERBOSE(...) do{if(FLAGS_verbose){fprintf(stderr, __VA_ARGS__);fflush(stderr);}}while(0) #define WARN(...) do{fprintf(stderr, __VA_ARGS__);}while(0) #define FATAL(...) do{fprintf(stderr, __VA_ARGS__);abort();}while(0) PublicKey::PublicKey(const std::string& filename) : key_(NULL), publicOnly_(true) { EVP_PKEY* pkey = NULL; BIO* bio = BIO_new(BIO_s_file()); OpenSSL_add_all_ciphers(); // needed to decrypt PEM. if (BIO_read_filename(bio, filename.c_str()) == 1) { pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); if (pkey) { publicOnly_ = false; } else { // Try read as public key. (void)BIO_reset(bio); pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); if (pkey) { VERBOSE("read public key only, assuming gnubby for signing..\n"); } } } if (!pkey) { WARN("loadKey: failed to load RSA key from '%s'\n", filename.c_str()); } BIO_free_all(bio); key_ = pkey; } PublicKey::~PublicKey() { if (key_) { EVP_PKEY_free(key_); key_ = NULL; } } bool PublicKey::ok() { return key_ != NULL; } size_t PublicKey::nwords() { RSA* rsa = EVP_PKEY_get1_RSA(key_); size_t result = (BN_num_bytes(rsa->n) + 3) / 4; RSA_free(rsa); return result; } uint32_t PublicKey::public_exponent() { RSA* rsa = EVP_PKEY_get1_RSA(key_); uint32_t result = BN_get_word(rsa->e); RSA_free(rsa); return result; } uint32_t PublicKey::n0inv() { RSA* rsa = EVP_PKEY_get1_RSA(key_); BN_CTX* ctx = BN_CTX_new(); BIGNUM* r = BN_new(); BIGNUM* rem = BN_new(); BIGNUM* n0inv = BN_new(); BN_set_bit(r, 32); // 2**32 BN_div(NULL, rem, rsa->n, r, ctx); // low 32 bit BN_mod_inverse(n0inv, rem, r, ctx); uint32_t result = 0 - BN_get_word(n0inv); BN_free(n0inv); BN_free(rem); BN_free(r); BN_CTX_free(ctx); RSA_free(rsa); return result; } void PublicKey::print(const char* tag, size_t nwords, BIGNUM* n) { BN_CTX* ctx = BN_CTX_new(); BIGNUM* N = BN_new(); BIGNUM* r = BN_new(); BIGNUM* d = BN_new(); BIGNUM* rem = BN_new(); BN_set_bit(r, 32); // 2^32 BN_copy(N, n); printf("const uint32_t %s[%lu + 1] = {", tag, nwords); printf("0x%08x, ", n0inv()); for (size_t i = 0; i < nwords; ++i) { if (i) printf(", "); BN_div(N, rem, N, r, ctx); printf("0x%08lx", BN_get_word(rem)); } printf("};\n"); BN_free(rem); BN_free(d); BN_free(r); BN_free(N); BN_CTX_free(ctx); } void PublicKey::print(const char* tag, size_t nwords, uint8_t* data, size_t len) { BIGNUM* n = BN_bin2bn(data, len, NULL); print(tag, nwords, n); BN_free(n); } void PublicKey::print(const char* tag) { RSA* rsa = EVP_PKEY_get1_RSA(key_); print(tag, rwords(), rsa->n); RSA_free(rsa); } /*static*/ void PublicKey::toArray(uint32_t* dst, size_t nwords, BIGNUM* n) { BN_CTX* ctx = BN_CTX_new(); BIGNUM* N = BN_new(); BIGNUM* r = BN_new(); BIGNUM* d = BN_new(); BIGNUM* rem = BN_new(); BN_set_bit(r, 32); // 2^32 BN_copy(N, n); for (size_t i = 0; i < nwords; ++i) { BN_div(N, rem, N, r, ctx); *dst++ = BN_get_word(rem); } BN_free(rem); BN_free(d); BN_free(r); BN_free(N); BN_CTX_free(ctx); } void PublicKey::modToArray(uint32_t* dst, size_t nwords) { RSA* rsa = EVP_PKEY_get1_RSA(key_); toArray(dst, nwords, rsa->n); RSA_free(rsa); } int PublicKey::encrypt(uint8_t* msg, int msglen, uint8_t* out) { RSA* rsa = EVP_PKEY_get1_RSA(key_); int result = RSA_public_encrypt(msglen, msg, out, rsa, RSA_PKCS1_OAEP_PADDING); RSA_free(rsa); return result; } int PublicKey::decrypt(uint8_t* msg, int msglen, uint8_t* out) { RSA* rsa = EVP_PKEY_get1_RSA(key_); int result = RSA_private_decrypt(msglen, msg, out, rsa, RSA_PKCS1_OAEP_PADDING); RSA_free(rsa); return result; } int PublicKey::raw(uint8_t* in, int inlen, BIGNUM** out) { RSA* rsa = EVP_PKEY_get1_RSA(key_); BN_CTX* ctx = BN_CTX_new(); BIGNUM* m = BN_new(); BIGNUM* r = BN_new(); BN_bin2bn(in, inlen, m); int result = BN_mod_exp(r, m, rsa->d, rsa->n, ctx); if (result == 1) { *out = BN_dup(r); } BN_free(r); BN_free(m); BN_CTX_free(ctx); RSA_free(rsa); return result; } // Sign message. // Produces signature * R mod N (Montgomery format). // Returns 1 on success. int PublicKey::sign(const void* msg, size_t msglen, BIGNUM** output) { int result = 0; EVP_MD_CTX* ctx = NULL; BN_CTX* bnctx = NULL; BIGNUM* tmp = NULL; RSA* rsa = NULL; uint8_t* sig = NULL; unsigned int siglen = 0; unsigned int tmplen = EVP_PKEY_size(key_); ctx = EVP_MD_CTX_create(); if (!ctx) goto __fail; EVP_MD_CTX_init(ctx); EVP_DigestInit(ctx, EVP_sha256()); if (EVP_DigestUpdate(ctx, msg, msglen) != 1) goto __fail; sig = (uint8_t*)malloc(tmplen); if (publicOnly_) { if (nwords() == 64) { // 2048 bit public key : gnubby fprintf(stderr, "gnubby signing.."); fflush(stderr); Gnubby gnubby; result = gnubby.sign(ctx, sig, &siglen, key_); fprintf(stderr, "Gnubby.sign: %d\n", result); } else { // other public key : best have signature prefilled fprintf(stderr, "WARNING: public key size %lu; assuming preloaded signature\n", nwords()); fprintf(stderr, " Likely you are trying to use the real rom key, try the -dev flavor\n"); fflush(stderr); siglen = BN_bn2bin(*output, sig); result = 1; } } else { VERBOSE("ossl signing.."); result = EVP_SignFinal(ctx, sig, &siglen, key_); VERBOSE("EVP_SignFinal: %d\n", result); } if (result != 1) goto __fail; tmp = BN_bin2bn(sig, siglen, NULL); // compute R*sig mod N rsa = EVP_PKEY_get1_RSA(key_); if (BN_lshift(tmp, tmp, rwords() * 32) != 1) goto __fail; bnctx = BN_CTX_new(); if (BN_mod(tmp, tmp, rsa->n, bnctx) != 1) goto __fail; *output = BN_dup(tmp); __fail: if (tmp) BN_free(tmp); if (rsa) RSA_free(rsa); if (sig) free(sig); if (ctx) EVP_MD_CTX_destroy(ctx); if (bnctx) BN_CTX_free(bnctx); return result; } int PublicKey::writeToGnubby() { if (publicOnly_) return -1; RSA* rsa = EVP_PKEY_get1_RSA(key_); Gnubby gnubby; int result = gnubby.write(rsa); RSA_free(rsa); return result; }