diff options
Diffstat (limited to 'projects/openvpn/fuzz_crypto.c')
-rw-r--r-- | projects/openvpn/fuzz_crypto.c | 257 |
1 files changed, 257 insertions, 0 deletions
diff --git a/projects/openvpn/fuzz_crypto.c b/projects/openvpn/fuzz_crypto.c new file mode 100644 index 000000000..e9a851be0 --- /dev/null +++ b/projects/openvpn/fuzz_crypto.c @@ -0,0 +1,257 @@ +/* Copyright 2021 Google LLC +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + + +#include "config.h" +#include "syshead.h" + +#include <openssl/x509.h> +#include <openssl/x509v3.h> +#include <openssl/ssl.h> +#include <openssl/err.h> + +#include "fuzz_verify_cert.h" +#include "misc.h" +#include "manage.h" +#include "otime.h" +#include "base64.h" +#include "ssl_verify.h" +#include "ssl_verify_backend.h" + +#include "fuzz_randomizer.h" + +static void key_ctx_update_implicit_iv(struct key_ctx *ctx, uint8_t *key, + size_t key_len) { + const cipher_kt_t *cipher_kt = cipher_ctx_get_cipher_kt(ctx->cipher); + + /* Only use implicit IV in AEAD cipher mode, where HMAC key is not used */ + if (cipher_kt_mode_aead(cipher_kt)) { + size_t impl_iv_len = 0; + ASSERT(cipher_kt_iv_size(cipher_kt) >= OPENVPN_AEAD_MIN_IV_LEN); + impl_iv_len = cipher_kt_iv_size(cipher_kt) - sizeof(packet_id_type); + ASSERT(impl_iv_len <= OPENVPN_MAX_IV_LENGTH); + ASSERT(impl_iv_len <= key_len); + memcpy(ctx->implicit_iv, key, impl_iv_len); + ctx->implicit_iv_len = impl_iv_len; + } +} + +static int init_frame(struct frame *frame) { + frame->link_mtu = fuzz_randomizer_get_int(100, 1000); + frame->extra_buffer = fuzz_randomizer_get_int(100, 1000); + frame->link_mtu_dynamic = fuzz_randomizer_get_int(100, 1000); + frame->extra_frame = fuzz_randomizer_get_int(100, 1000); + frame->extra_tun = fuzz_randomizer_get_int(100, 1000); + frame->extra_link = fuzz_randomizer_get_int(100, 1000); + frame->align_flags = 0; + frame->align_adjust = 0; + if (TUN_MTU_SIZE(frame) <= 0) { + return -1; + } + return 0; +} + +int LLVMFuzzerInitialize(int *argc, char ***argv) { + OPENSSL_malloc_init(); + SSL_library_init(); + ERR_load_crypto_strings(); + + OpenSSL_add_all_algorithms(); + OpenSSL_add_ssl_algorithms(); + + SSL_load_error_strings(); + return 0; +} + +int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { + fuzz_random_init(data, size); + fuzz_success = 1; + bool key_ctx_dec_initialized = false; + bool key_ctx_enc_initialized = false; + struct key_ctx key_ctx_dec; + memset(&key_ctx_dec, 0, sizeof(struct key_ctx)); + struct key_ctx key_ctx_enc; + memset(&key_ctx_enc, 0, sizeof(struct key_ctx)); + + struct gc_arena gc; + struct tls_session *session = NULL; + X509 *x509 = NULL; + gc = gc_new(); + + gb_init(); + + // Read key file + struct key2 key2; + char *keydata = gb_get_random_string(); + read_key_file(&key2, keydata, RKF_INLINE); + + // init key type + struct key_type kt; + memset(&kt, 0, sizeof(struct key_type)); + + char *ciphername = gb_get_random_string(); + char *authname = gb_get_random_string(); + bool key_type_initialized = false; + + if (strcmp(ciphername, "AES-256-GCM") == 0 || + strcmp(ciphername, "AES-128-GCM") == 0 || + strcmp(ciphername, "AES-192-GCM") == 0 || + strcmp(ciphername, "CAMELLIA-128-CFB128") == 0) { + + int v = fuzz_randomizer_get_int(0, 1); + if (v == 0) { + init_key_type(&kt, ciphername, authname, true, 0); + } else { + init_key_type(&kt, ciphername, authname, false, 0); + } + key_type_initialized = true; + } + + if (fuzz_success == 0) { + goto cleanup; + } + + // Generate key. + // Identify which one we should do, read or generate a random key. + int c = fuzz_randomizer_get_int(0, 1); + const uint8_t d[1024]; + int key_read = 0; + struct key key; + if (c == 0) { + if (fuzz_get_random_data(d, 1024) != 1024) { + struct buffer buf = alloc_buf(1024); + buf_write(&buf, d, 1024); + if (read_key(&key, &kt, &buf) == 1) { + key_read = 1; + } + free_buf(&buf); + } + } + else { + if (key_type_initialized == true) { + generate_key_random(&key, &kt); + } + } + + if (fuzz_success == 0) { + goto cleanup; + } + key_read = 1; + + // init decryption context + if (key_type_initialized && key_read) { + init_key_ctx(&key_ctx_dec, &key, &kt, OPENVPN_OP_DECRYPT, "x"); + key_ctx_update_implicit_iv(&key_ctx_dec, &(key.hmac), MAX_HMAC_KEY_LENGTH); + key_ctx_dec_initialized = true; + } + + // init encryption context + if (key_type_initialized && key_read) { + init_key_ctx(&key_ctx_enc, &key, &kt, OPENVPN_OP_DECRYPT, "x"); + key_ctx_update_implicit_iv(&key_ctx_enc, &(key.hmac), MAX_HMAC_KEY_LENGTH); + key_ctx_enc_initialized = true; + } + + // perform encryption + struct frame frame; + memset(&frame, 0, sizeof(struct frame)); + if (key_ctx_enc_initialized == true && key_ctx_dec_initialized == true && + init_frame(&frame) == 0) { + struct crypto_options opt; + memset(&opt, 0, sizeof(opt)); + opt.pid_persist = NULL; + opt.key_ctx_bi.encrypt = key_ctx_enc; + opt.key_ctx_bi.decrypt = key_ctx_dec; + opt.key_ctx_bi.initialized = true; + opt.packet_id.rec.initialized = true; + opt.packet_id.rec.seq_list = NULL; + opt.packet_id.rec.name = NULL; + + void *buf_p; + + struct buffer encrypt_workspace = alloc_buf_gc(BUF_SIZE(&(frame)), &gc); + struct buffer work = alloc_buf_gc(BUF_SIZE(&(frame)), &gc); + struct buffer src = alloc_buf_gc(TUN_MTU_SIZE(&(frame)), &gc); + struct buffer buf = clear_buf(); + + int x = fuzz_randomizer_get_int(1, TUN_MTU_SIZE(&frame)); + + ASSERT(buf_init(&work, FRAME_HEADROOM(&(frame)))); + ASSERT(buf_init(&src, 0)); + src.len = x; + ASSERT(rand_bytes(BPTR(&src), BLEN(&src))); + + buf = work; + buf_p = buf_write_alloc(&buf, BLEN(&src)); + ASSERT(buf_p); + memcpy(buf_p, BPTR(&src), BLEN(&src)); + + ASSERT(buf_init(&encrypt_workspace, FRAME_HEADROOM(&(frame)))); + + openvpn_encrypt(&buf, encrypt_workspace, &opt); + } + + // perform decryption + memset(&frame, 0, sizeof(struct frame)); + if (key_ctx_dec_initialized == true && key_ctx_enc_initialized == true && + init_frame(&frame) == 0) { + struct crypto_options opt; + memset(&opt, 0, sizeof(opt)); + opt.pid_persist = NULL; + opt.key_ctx_bi.encrypt = key_ctx_enc; + opt.key_ctx_bi.decrypt = key_ctx_dec; + opt.key_ctx_bi.initialized = true; + opt.packet_id.rec.initialized = true; + opt.packet_id.rec.seq_list = NULL; + opt.packet_id.rec.name = NULL; + + void *buf_p; + + struct buffer decrypt_workspace = alloc_buf_gc(BUF_SIZE(&(frame)), &gc); + struct buffer work = alloc_buf_gc(BUF_SIZE(&(frame)), &gc); + struct buffer src = alloc_buf_gc(TUN_MTU_SIZE(&(frame)), &gc); + struct buffer buf = clear_buf(); + + int x = fuzz_randomizer_get_int(1, TUN_MTU_SIZE(&frame)); + + ASSERT(buf_init(&work, FRAME_HEADROOM(&(frame)))); + ASSERT(buf_init(&src, 0)); + src.len = x; + ASSERT(rand_bytes(BPTR(&src), BLEN(&src))); + + buf = work; + buf_p = buf_write_alloc(&buf, BLEN(&src)); + ASSERT(buf_p); + memcpy(buf_p, BPTR(&src), BLEN(&src)); + + ASSERT(buf_init(&decrypt_workspace, FRAME_HEADROOM(&(frame)))); + + openvpn_decrypt(&buf, decrypt_workspace, &opt, &frame, BPTR(&buf)); + } + +cleanup: + // cleanup + gc_free(&gc); + + if (key_ctx_dec_initialized == true) { + free_key_ctx(&key_ctx_dec); + } + + if (key_ctx_enc_initialized == true) { + free_key_ctx(&key_ctx_enc); + } + fuzz_random_destroy(); + + gb_cleanup(); + + return 0; +} |