X-Git-Url: http://git.tuebingen.mpg.de/?a=blobdiff_plain;f=openssl.c;h=a5a6a17576989ed51fbe36c116effb3ae0679f1a;hb=8e875411f29b017313482637cbc5b74a69f4ddde;hp=13550e7a9f34e1771932d05ef7fea5fed8c109d6;hpb=83c4cb37c39f01d038c808132a6ec990a0c75aa3;p=paraslash.git diff --git a/openssl.c b/openssl.c index 13550e7a..a5a6a175 100644 --- a/openssl.c +++ b/openssl.c @@ -22,17 +22,25 @@ 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); } @@ -94,88 +102,155 @@ static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result) return bnsize + 4; } -static int read_public_key(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 free_rsa; + return ret; p += ret; ret = read_bignum(p, end - p, &n); - if (ret < 0) - goto free_e; -#ifdef HAVE_RSA_SET0_KEY - RSA_set0_key(rsa, n, e, NULL); -#else - rsa->n = n; - rsa->e = e; -#endif - *result = rsa; - return 1; -free_e: + 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); -free_rsa: - RSA_free(rsa); - return ret; + BN_free(n); + if (ret < 0) + 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, RSA **rsa) +static int read_pem_private_key(const char *path, struct asymmetric_key *priv) { - 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 *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 +free_bio: BIO_free(bio); - return *rsa? RSA_size(*rsa) : -E_PRIVATE_KEY; + return ret; } static int read_openssh_private_key(const unsigned char *blob, - const unsigned char *end, RSA **result) + const unsigned char *end, struct asymmetric_key *priv) { int ret; - RSA *rsa; - BN_CTX *ctx; BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */ - BIGNUM *dmp1, *dmq1; /* these will be computed */ - BIGNUM *tmp; const unsigned char *cp = blob; - rsa = RSA_new(); - if (!rsa) - return -E_BIGNUM; - ret = -E_BIGNUM; - tmp = BN_new(); - if (!tmp) - goto free_rsa; - ctx = BN_CTX_new(); - if (!ctx) - goto free_tmp; - dmp1 = BN_new(); - if (!dmp1) - goto free_ctx; - dmq1 = BN_new(); - if (!dmq1) - goto free_dmp1; ret = read_bignum(cp, end - cp, &n); if (ret < 0) - goto free_dmq1; + return ret; cp += ret; ret = read_bignum(cp, end - cp, &e); if (ret < 0) @@ -196,33 +271,29 @@ static int read_openssh_private_key(const unsigned char *blob, ret = read_bignum(cp, end - cp, &q); if (ret < 0) goto free_p; - ret = -E_BIGNUM; - if (!BN_sub(tmp, q, BN_value_one())) - goto free_q; - if (!BN_mod(dmp1, d, tmp, ctx)) - goto free_q; - if (!BN_sub(tmp, q, BN_value_one())) - goto free_q; - if (!BN_mod(dmq1, d, tmp, ctx)) - goto free_q; -#ifdef HAVE_RSA_SET0_KEY - RSA_set0_key(rsa, n, e, d); - RSA_set0_factors(rsa, p, q); - RSA_set0_crt_params(rsa, dmp1, dmq1, iqmp); +#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 - rsa->n = n; - rsa->e = e; - rsa->d = d; - rsa->iqmp = iqmp; - rsa->p = p; - rsa->q = q; - rsa->dmp1 = dmp1; - rsa->dmq1 = dmq1; -#endif - *result = rsa; - ret = 1; - goto free_ctx; -free_q: + 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; +#endif /* HAVE_OSSL_PARAM */ BN_clear_free(q); free_p: BN_clear_free(p); @@ -234,27 +305,15 @@ free_e: BN_free(e); free_n: BN_free(n); -free_dmq1: - BN_clear_free(dmq1); -free_dmp1: - BN_clear_free(dmp1); -free_ctx: - BN_CTX_free(ctx); -free_tmp: - BN_clear_free(tmp); -free_rsa: - if (ret < 0) - RSA_free(rsa); return ret; } -static int get_private_key(const char *path, RSA **rsa) +static int get_private_key(const char *path, struct asymmetric_key *priv) { int ret; unsigned char *blob, *end; size_t blob_size; - *rsa = NULL; ret = decode_private_key(path, &blob, &blob_size); if (ret < 0) return ret; @@ -264,9 +323,9 @@ static int get_private_key(const char *path, RSA **rsa) 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, rsa); + ret = read_openssh_private_key(blob + ret, end, priv); } else - ret = read_pem_private_key(path, rsa); + ret = read_pem_private_key(path, priv); free_blob: free(blob); return ret; @@ -277,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 *pub = alloc(sizeof(*pub)); + struct asymmetric_key *pub; ret = decode_public_key(key_file, &blob, &decoded_size); if (ret < 0) - goto out; - ret = read_public_key(blob + ret, decoded_size - ret, &pub->rsa); - if (ret < 0) - goto free_blob; - ret = RSA_size(pub->rsa); - assert(ret > 0); - *result = pub; -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(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 *pub) { if (!pub) return; +#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); } -int apc_priv_decrypt(const char *key_file, unsigned char *outbuf, +#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, 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 = alloc(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. @@ -331,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 */ - - if (flen < 0) - return -E_ENCRYPT; - ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa, + int ret; +#ifdef HAVE_OSSL_PARAM /* openssl-3 */ + EVP_PKEY_CTX *ctx; + size_t outlen; + + *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 { @@ -363,7 +482,7 @@ struct stream_cipher *sc_new(const unsigned char *data, int len) 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; @@ -403,8 +522,11 @@ 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) { - EVP_MD_CTX *c = EVP_MD_CTX_new(); - int ret = EVP_DigestInit_ex(c, EVP_sha1(), NULL); + 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); @@ -415,8 +537,11 @@ void hash_function(const char *data, unsigned long len, unsigned char *hash) void hash2_function(const char *data, unsigned long len, unsigned char *hash) { - EVP_MD_CTX *c = EVP_MD_CTX_new(); - int ret = EVP_DigestInit_ex(c, EVP_sha256(), NULL); + 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);