X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=openssl.c;h=13550e7a9f34e1771932d05ef7fea5fed8c109d6;hb=refs%2Fheads%2Fnext;hp=7d5bb25d53cf73009803fe0e62ca15d0167a05cb;hpb=3e16770594ee8267db0523ec733d0af794f277ff;p=paraslash.git diff --git a/openssl.c b/openssl.c index 7d5bb25d..a5a6a175 100644 --- a/openssl.c +++ b/openssl.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "para.h" #include "error.h" @@ -21,27 +22,31 @@ struct asymmetric_key { RSA *rsa; + EVP_PKEY *pkey; + EVP_PKEY_CTX *ctx; }; +static int openssl_perror(const char *pfx) +{ + unsigned long err = ERR_get_error(); + PARA_ERROR_LOG("%s: \"%s\"\n", pfx, ERR_reason_error_string(err)); + return -E_OPENSSL; +} + void get_random_bytes_or_die(unsigned char *buf, int num) { - unsigned long err; + int ret; - /* RAND_bytes() returns 1 on success, 0 otherwise. */ - if (RAND_bytes(buf, num) == 1) + if (RAND_bytes(buf, num) == 1) /* success */ return; - err = ERR_get_error(); - PARA_EMERG_LOG("%s\n", ERR_reason_error_string(err)); + ret = openssl_perror("RAND_bytes"); + PARA_EMERG_LOG("%s\n", strerror(-ret)); exit(EXIT_FAILURE); } /* - * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Seed the PRNG - * used by random(3) with a random seed obtained from SSL. If /dev/urandom is - * not readable, the function calls exit(). - * - * \sa RAND_load_file(3), \ref get_random_bytes_or_die(), srandom(3), - * random(3), \ref para_random(). + * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Then seed the + * PRNG used by random(3) with a random seed obtained from SSL. */ void crypt_init(void) { @@ -57,27 +62,12 @@ void crypt_init(void) void crypt_shutdown(void) { - CRYPTO_cleanup_all_ex_data(); -} - -static int get_private_key(const char *path, RSA **rsa) -{ - EVP_PKEY *pkey; - BIO *bio = BIO_new(BIO_s_file()); - - *rsa = NULL; - if (!bio) - return -E_PRIVATE_KEY; - if (BIO_read_filename(bio, path) <= 0) - goto bio_free; - pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - if (!pkey) - goto bio_free; - *rsa = EVP_PKEY_get1_RSA(pkey); - EVP_PKEY_free(pkey); -bio_free: - BIO_free(bio); - return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY; +#ifdef HAVE_OPENSSL_THREAD_STOP /* openssl-1.1 or later */ + OPENSSL_thread_stop(); +#else /* openssl-1.0 */ + ERR_remove_thread_state(NULL); +#endif + EVP_cleanup(); } /* @@ -112,33 +102,232 @@ static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result) return bnsize + 4; } -static int read_rsa_bignums(const unsigned char *blob, int blen, RSA **result) +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + +static int generate_private_pkey(struct asymmetric_key *priv, + const BIGNUM *n, const BIGNUM *e, const BIGNUM *d, + const BIGNUM *p, const BIGNUM *q) { - int ret; - RSA *rsa; - BIGNUM *n, *e; + const BIGNUM *bignums[] = {n, e, d, p, q}; + const char *strings[] = {"n", "e", "d", "p", "q"}; + int ret, bytes[ARRAY_SIZE(bignums)]; + unsigned char *bufs[ARRAY_SIZE(bignums)]; + OSSL_PARAM params[ARRAY_SIZE(bignums) + 1]; + /* + * Convert bignums to buffers for OSSL_PARAM_construct_BN() and init + * params[]. + */ + for (int i = 0; i < ARRAY_SIZE(bignums); i++) { + bytes[i] = BN_num_bytes(bignums[i]); + PARA_DEBUG_LOG("%s: %d bits\n", strings[i], bytes[i] * 8); + bufs[i] = alloc(bytes[i]); + assert(BN_bn2nativepad(bignums[i], bufs[i], bytes[i]) > 0); + params[i] = OSSL_PARAM_construct_BN(strings[i], bufs[i], + bytes[i]); + } + params[ARRAY_SIZE(bignums)] = OSSL_PARAM_construct_end(); + /* Transfer buffers to openssl to create the pkey from it */ + priv->ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + assert(priv->ctx); + assert(EVP_PKEY_fromdata_init(priv->ctx) > 0); + ret = EVP_PKEY_fromdata(priv->ctx, &priv->pkey, + EVP_PKEY_KEYPAIR, params); + for (int i = 0; i < ARRAY_SIZE(bignums); i++) + free(bufs[i]); + if (ret <= 0) { + EVP_PKEY_CTX_free(priv->ctx); + return openssl_perror("EVP_PKEY_fromdata()"); + } + assert(priv->pkey); + return BN_num_bytes(n) * 8; +} + +/* + * Convert bignumns e and n to a pkey and context. + */ +static int generate_public_pkey(struct asymmetric_key *pub, + const BIGNUM *e, const BIGNUM *n) +{ + unsigned char *ebuf, *nbuf; + int ret, ebytes = BN_num_bytes(e), nbytes = BN_num_bytes(n); + OSSL_PARAM params[3]; + + /* Convert e and n to a buffer for OSSL_PARAM_construct_BN() */ + ebuf = alloc(ebytes); + assert(BN_bn2nativepad(e, ebuf, ebytes) > 0); + nbuf = alloc(nbytes); + assert(BN_bn2nativepad(n, nbuf, nbytes) > 0); + /* Init params[] with {e,n}buf and create the pkey from it */ + params[0] = OSSL_PARAM_construct_BN("e", ebuf, ebytes); + params[1] = OSSL_PARAM_construct_BN("n", nbuf, nbytes); + params[2] = OSSL_PARAM_construct_end(); + pub->ctx = EVP_PKEY_CTX_new_from_name(NULL, "RSA", NULL); + assert(pub->ctx); + assert(EVP_PKEY_fromdata_init(pub->ctx) > 0); + ret = EVP_PKEY_fromdata(pub->ctx, &pub->pkey, EVP_PKEY_PUBLIC_KEY, + params); + free(nbuf); + free(ebuf); + if (ret <= 0) { + EVP_PKEY_CTX_free(pub->ctx); + return openssl_perror("EVP_PKEY_fromdata()"); + } + assert(pub->pkey); + return nbytes * 8; +} + +#endif /* HAVE_OSSL_PARAM */ + +static int read_public_key(const unsigned char *blob, size_t blen, + struct asymmetric_key *pub) +{ + int ret, bits; const unsigned char *p = blob, *end = blob + blen; + BIGNUM *e, *n; - rsa = RSA_new(); - if (!rsa) - return -E_BIGNUM; ret = read_bignum(p, end - p, &e); if (ret < 0) - goto fail; + return ret; p += ret; ret = read_bignum(p, end - p, &n); + if (ret < 0) { + BN_free(e); + return ret; + } + bits = BN_num_bytes(n) * 8; + PARA_DEBUG_LOG("modulus: %d bits\n", bits); +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + ret = generate_public_pkey(pub, e, n); + BN_free(e); + BN_free(n); if (ret < 0) - goto fail; -#ifdef HAVE_RSA_SET0_KEY - RSA_set0_key(rsa, n, e, NULL); -#else - rsa->n = n; - rsa->e = e; + return ret; +#else /* openssl < 3.0 */ + pub->rsa = RSA_new(); + assert(pub->rsa); + #if HAVE_RSA_SET0_KEY /* openssl-1.1 */ + RSA_set0_key(pub->rsa, n, e, NULL); + #else /* openssl-1.0 */ + pub->rsa->n = n; + pub->rsa->e = e; + #endif + /* e and n are now owned by openssl */ +#endif /* HAVE_OSSL_PARAM */ + return bits; +} + +static int read_pem_private_key(const char *path, struct asymmetric_key *priv) +{ + BIO *bio; + int ret; + + assert((bio = BIO_new(BIO_s_file()))); + ret = BIO_read_filename(bio, path); + if (ret <= 0) { + priv->pkey = NULL; + ret = openssl_perror("BIO_read_filename"); + goto free_bio; + } + priv->pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!priv->pkey) { + ret = openssl_perror("PEM_read_bio_PrivateKey"); + goto free_bio; + } +#ifndef HAVE_OSSL_PARAM /* openssl-1 */ + priv->rsa = EVP_PKEY_get1_RSA(priv->pkey); #endif - *result = rsa; +free_bio: + BIO_free(bio); + return ret; +} + +static int read_openssh_private_key(const unsigned char *blob, + const unsigned char *end, struct asymmetric_key *priv) +{ + int ret; + BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */ + const unsigned char *cp = blob; + + ret = read_bignum(cp, end - cp, &n); + if (ret < 0) + return ret; + cp += ret; + ret = read_bignum(cp, end - cp, &e); + if (ret < 0) + goto free_n; + cp += ret; + ret = read_bignum(cp, end - cp, &d); + if (ret < 0) + goto free_e; + cp += ret; + ret = read_bignum(cp, end - cp, &iqmp); + if (ret < 0) + goto free_d; + cp += ret; + ret = read_bignum(cp, end - cp, &p); + if (ret < 0) + goto free_iqmp; + cp += ret; + ret = read_bignum(cp, end - cp, &q); + if (ret < 0) + goto free_p; +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + /* + * Ignore iqmp, the coefficient for Chinese remainder theorem. It is + * dispensable because it can be derived from the other values. Passing + * it to the EVP API results in a memory leak. + */ + ret = generate_private_pkey(priv, n, e, d, p, q); +#else + assert((priv->rsa = RSA_new())); + #ifdef HAVE_RSA_SET0_KEY + RSA_set0_key(priv->rsa, n, e, d); + RSA_set0_factors(priv->rsa, p, q); + RSA_set0_crt_params(priv->rsa, NULL, NULL, iqmp); + #else + priv->rsa->n = n; + priv->rsa->e = e; + priv->rsa->d = d; + priv->rsa->iqmp = iqmp; + priv->rsa->p = p; + priv->rsa->q = q; + #endif return 1; -fail: - RSA_free(rsa); +#endif /* HAVE_OSSL_PARAM */ + BN_clear_free(q); +free_p: + BN_clear_free(p); +free_iqmp: + BN_clear_free(iqmp); +free_d: + BN_clear_free(d); +free_e: + BN_free(e); +free_n: + BN_free(n); + return ret; +} + +static int get_private_key(const char *path, struct asymmetric_key *priv) +{ + int ret; + unsigned char *blob, *end; + size_t blob_size; + + ret = decode_private_key(path, &blob, &blob_size); + if (ret < 0) + return ret; + end = blob + blob_size; + if (ret == PKT_OPENSSH) { + ret = find_openssh_bignum_offset(blob, blob_size); + if (ret < 0) + goto free_blob; + PARA_INFO_LOG("reading RSA params at offset %d\n", ret); + ret = read_openssh_private_key(blob + ret, end, priv); + } else + ret = read_pem_private_key(path, priv); +free_blob: + free(blob); return ret; } @@ -147,53 +336,84 @@ int apc_get_pubkey(const char *key_file, struct asymmetric_key **result) unsigned char *blob; size_t decoded_size; int ret; - struct asymmetric_key *key = para_malloc(sizeof(*key)); + struct asymmetric_key *pub; - ret = decode_ssh_key(key_file, &blob, &decoded_size); - if (ret < 0) - goto out; - ret = read_rsa_bignums(blob + ret, decoded_size - ret, &key->rsa); + ret = decode_public_key(key_file, &blob, &decoded_size); if (ret < 0) - goto free_blob; - ret = RSA_size(key->rsa); - assert(ret > 0); - *result = key; -free_blob: + return ret; + pub = zalloc(sizeof(*pub)); /* ->pkey needs to start out zeroed */ + ret = read_public_key(blob + ret, decoded_size - ret, pub); free(blob); -out: if (ret < 0) { - free(key); + free(pub); *result = NULL; PARA_ERROR_LOG("can not load key %s\n", key_file); + return ret; } - return ret; + PARA_NOTICE_LOG("loaded %d bit key from %s\n", ret, key_file); + *result = pub; + return ret / 8; } -void apc_free_pubkey(struct asymmetric_key *key) +void apc_free_pubkey(struct asymmetric_key *pub) { - if (!key) + if (!pub) return; - RSA_free(key->rsa); - free(key); +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + EVP_PKEY_CTX_free(pub->ctx); + EVP_PKEY_free(pub->pkey); +#else + RSA_free(pub->rsa); +#endif + free(pub); +} + +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ +static int pkey_priv_decrypt(const struct asymmetric_key *priv, + unsigned char **outbuf, unsigned char *inbuf, int inlen) +{ + EVP_PKEY_CTX *ctx; + size_t outlen; + + assert((ctx = EVP_PKEY_CTX_new(priv->pkey, NULL))); + assert((EVP_PKEY_decrypt_init(ctx) > 0)); + assert(EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) > 0); + if (EVP_PKEY_decrypt(ctx, NULL, &outlen, inbuf, inlen) <= 0) { + *outbuf = NULL; + EVP_PKEY_CTX_free(ctx); + return openssl_perror("EVP_PKEY_encrypt()"); + } + *outbuf = alloc(outlen); + assert((EVP_PKEY_decrypt(ctx, *outbuf, &outlen, inbuf, inlen) > 0)); + EVP_PKEY_CTX_free(ctx); + PARA_INFO_LOG("wrote %zu decrypted data bytes\n", outlen); + return outlen; } +#endif /* HAVE_OSSL_PARAM */ -int apc_priv_decrypt(const char *key_file, unsigned char *outbuf, +int apc_priv_decrypt(const char *key_file, unsigned char **outbuf, unsigned char *inbuf, int inlen) { struct asymmetric_key *priv; int ret; + *outbuf = NULL; ret = check_private_key_file(key_file); if (ret < 0) return ret; if (inlen < 0) return -E_RSA; - priv = para_malloc(sizeof(*priv)); - ret = get_private_key(key_file, &priv->rsa); + priv = zalloc(sizeof(*priv)); /* ->pkey needs to start out zeroed */ + ret = get_private_key(key_file, priv); if (ret < 0) { free(priv); return ret; } +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + ret = pkey_priv_decrypt(priv, outbuf, inbuf, inlen); + EVP_PKEY_CTX_free(priv->ctx); + EVP_PKEY_free(priv->pkey); +#else /* * RSA is vulnerable to timing attacks. Generate a random blinding * factor to protect against this kind of attack. @@ -201,27 +421,56 @@ int apc_priv_decrypt(const char *key_file, unsigned char *outbuf, ret = -E_BLINDING; if (RSA_blinding_on(priv->rsa, NULL) == 0) goto out; - ret = RSA_private_decrypt(inlen, inbuf, outbuf, priv->rsa, + *outbuf = alloc(RSA_size(priv->rsa)); + ret = RSA_private_decrypt(inlen, inbuf, *outbuf, priv->rsa, RSA_PKCS1_OAEP_PADDING); RSA_blinding_off(priv->rsa); - if (ret <= 0) + if (ret <= 0) { + free(*outbuf); + *outbuf = NULL; ret = -E_DECRYPT; + } out: RSA_free(priv->rsa); +#endif free(priv); return ret; } int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf, - unsigned len, unsigned char *outbuf) + unsigned len, unsigned char **outbuf) { - int ret, flen = len; /* RSA_public_encrypt expects a signed int */ + int ret; +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + EVP_PKEY_CTX *ctx; + size_t outlen; - if (flen < 0) - return -E_ENCRYPT; - ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa, + *outbuf = NULL; + assert((ctx = EVP_PKEY_CTX_new(pub->pkey, NULL))); + assert((EVP_PKEY_encrypt_init(ctx) > 0)); + assert((EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_OAEP_PADDING) > 0)); + if (EVP_PKEY_encrypt(ctx, NULL, &outlen, inbuf, len) <= 0) { + ret = openssl_perror("EVP_PKEY_encrypt()"); + goto free_ctx; + } + *outbuf = alloc(outlen); + assert((EVP_PKEY_encrypt(ctx, *outbuf, &outlen, inbuf, len) > 0)); + PARA_INFO_LOG("wrote %zu encrypted data bytes\n", outlen); + ret = outlen; +free_ctx: + EVP_PKEY_CTX_free(ctx); + return ret; +#else /* openssl < 3.0 */ + *outbuf = alloc(RSA_size(pub->rsa)); + ret = RSA_public_encrypt((int)len, inbuf, *outbuf, pub->rsa, RSA_PKCS1_OAEP_PADDING); - return ret < 0? -E_ENCRYPT : ret; + if (ret < 0) { + free(*outbuf); + *outbuf = NULL; + return -E_ENCRYPT; + } + return ret; +#endif /* HAVE_OSSL_PARAM */ } struct stream_cipher { @@ -230,10 +479,10 @@ struct stream_cipher { struct stream_cipher *sc_new(const unsigned char *data, int len) { - struct stream_cipher *sc = para_malloc(sizeof(*sc)); + struct stream_cipher *sc = alloc(sizeof(*sc)); assert(len >= 2 * AES_CRT128_BLOCK_SIZE); - sc->aes = EVP_CIPHER_CTX_new(); + assert((sc->aes = EVP_CIPHER_CTX_new())); EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data, data + AES_CRT128_BLOCK_SIZE); return sc; @@ -254,7 +503,7 @@ static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src, *dst = (typeof(*dst)) { /* Add one for the terminating zero byte. */ - .iov_base = para_malloc(inlen + 1), + .iov_base = alloc(inlen + 1), .iov_len = inlen }; ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen); @@ -273,8 +522,30 @@ void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst) void hash_function(const char *data, unsigned long len, unsigned char *hash) { - SHA_CTX c; - SHA1_Init(&c); - SHA1_Update(&c, data, len); - SHA1_Final(hash, &c); + int ret; + EVP_MD_CTX *c; + + assert((c = EVP_MD_CTX_new())); + ret = EVP_DigestInit_ex(c, EVP_sha1(), NULL); + assert(ret != 0); + ret = EVP_DigestUpdate(c, data, len); + assert(ret != 0); + ret = EVP_DigestFinal_ex(c, hash, NULL); + assert(ret != 0); + EVP_MD_CTX_free(c); +} + +void hash2_function(const char *data, unsigned long len, unsigned char *hash) +{ + int ret; + EVP_MD_CTX *c; + + assert((c = EVP_MD_CTX_new())); + ret = EVP_DigestInit_ex(c, EVP_sha256(), NULL); + assert(ret != 0); + ret = EVP_DigestUpdate(c, data, len); + assert(ret != 0); + ret = EVP_DigestFinal_ex(c, hash, NULL); + assert(ret != 0); + EVP_MD_CTX_free(c); }