From d7187e0b1ba0303dc493c4a75d9949cfca8ede98 Mon Sep 17 00:00:00 2001 From: Peter Wu Date: Mon, 30 Jul 2018 18:18:40 +0200 Subject: [PATCH] wsutil: Add Curve25519 ECDH (X25519) using Gcrypt The WireGuard dissector will need X25519 to enable decryption, add a Gcrypt implementation that implements the NaCl/Sodium interface. While inspired by the MPI example in t-cv25519.c, note subtle but important correctness/interoperability fixes: add a check for infinity (gcry_mpi_ec_get_affine) and handle short values from gcry_mpi_print. The last issue is ugly, perhaps the high level API (gcry_pk_decrypt) should be used instead (which < 2% slower than this MPI implementation). (Both issues were found through fuzzing.) As for alternative options, Sodium is superior but would be a new dependency. For some older performance and usability notes (comparing crypto_scalarmult_curve25519_base (note "_base") against others), see https://lists.gnupg.org/pipermail/gcrypt-devel/2018-July/004532.html Performance comparison on Ubuntu 18.04 (i7-3770) between Sodium 1.0.16 against Gcrypt 1.8.3 and Gcrypt 86e5e06a (git master, future 1.9.x) by computing 65536 times X25519(1, 8) via crypto_scalarmult_curve25519: Sodium (sandy2x): 1.4x faster than ref10 Sodium (ref10): 1 (baseline) Gcrypt (git): 5x slower than ref10, 7x slower than sandy2x Gcrypt (1.8.3): 17x ref10, 24x sandy2x (took 65 seconds) Change-Id: Ia54e73cc3cc469a6697554729aff4edd19f55630 Ping-Bug: 15011 Reviewed-on: https://code.wireshark.org/review/28987 Reviewed-by: Anders Broman --- debian/libwsutil0.symbols | 2 + wsutil/CMakeLists.txt | 2 + wsutil/curve25519.c | 103 ++++++++++++++++++++++++++++++++++++++ wsutil/curve25519.h | 41 +++++++++++++++ 4 files changed, 148 insertions(+) create mode 100644 wsutil/curve25519.c create mode 100644 wsutil/curve25519.h diff --git a/debian/libwsutil0.symbols b/debian/libwsutil0.symbols index 2149b023be..cf9c98ffaf 100644 --- a/debian/libwsutil0.symbols +++ b/debian/libwsutil0.symbols @@ -44,6 +44,8 @@ libwsutil.so.0 libwsutil0 #MINVER# create_tempfile@Base 1.12.0~rc1 create_timestamp@Base 2.5.0 crypt_des_ecb@Base 2.3.0 + crypto_scalarmult_curve25519@Base 2.9.0 + crypto_scalarmult_curve25519_base@Base 2.9.0 data_file_url@Base 2.3.0 decrypt_xtea_ecb@Base 2.5.0 decrypt_xtea_le_ecb@Base 2.5.0 diff --git a/wsutil/CMakeLists.txt b/wsutil/CMakeLists.txt index 87f111c1e3..2d0478f6a6 100644 --- a/wsutil/CMakeLists.txt +++ b/wsutil/CMakeLists.txt @@ -35,6 +35,7 @@ set(WSUTIL_PUBLIC_HEADERS crc16.h crc16-plain.h crc32.h + curve25519.h eax.h filesystem.h frequency-utils.h @@ -92,6 +93,7 @@ set(WSUTIL_COMMON_FILES crc7.c crc8.c crc11.c + curve25519.c dot11decrypt_wep.c eax.c filesystem.c diff --git a/wsutil/curve25519.c b/wsutil/curve25519.c new file mode 100644 index 0000000000..40ab112ff8 --- /dev/null +++ b/wsutil/curve25519.c @@ -0,0 +1,103 @@ +/* curve25519.c + * NaCl/Sodium-compatible API for Curve25519 cryptography. + * + * Copyright (c) 2018, Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#include "curve25519.h" + +#ifdef HAVE_X25519 +static inline void +copy_and_reverse(unsigned char *dest, const unsigned char *src, size_t n) +{ + for (size_t i = 0; i < n; i++) { + dest[n - 1 - i] = src[i]; + } +} + +static int +x25519_mpi(unsigned char *q, const unsigned char *n, gcry_mpi_t mpi_p) +{ + unsigned char priv_be[32]; + unsigned char result_be[32]; + size_t result_len = 0; + gcry_mpi_t mpi = NULL; + gcry_ctx_t ctx = NULL; + gcry_mpi_point_t P = NULL; + gcry_mpi_point_t Q = NULL; + int r = -1; + + /* Default to infinity (all zeroes). */ + memset(q, 0, 32); + + /* Keys are in little-endian, but gcry_mpi_scan expects big endian. Convert + * keys and ensure that the result is a valid Curve25519 secret scalar. */ + copy_and_reverse(priv_be, n, 32); + priv_be[0] &= 127; + priv_be[0] |= 64; + priv_be[31] &= 248; + gcry_mpi_scan(&mpi, GCRYMPI_FMT_USG, priv_be, 32, NULL); + + if (gcry_mpi_ec_new(&ctx, NULL, "Curve25519")) { + /* Should not happen, possibly out-of-memory. */ + goto leave; + } + + /* Compute Q = nP */ + Q = gcry_mpi_point_new(0); + P = gcry_mpi_point_set(NULL, mpi_p, NULL, GCRYMPI_CONST_ONE); + gcry_mpi_ec_mul(Q, mpi, P, ctx); + + /* Note: mpi is reused to store the result. */ + if (gcry_mpi_ec_get_affine(mpi, NULL, Q, ctx)) { + /* Infinity. */ + goto leave; + } + + if (gcry_mpi_print(GCRYMPI_FMT_USG, result_be, 32, &result_len, mpi)) { + /* Should not happen, possibly out-of-memory. */ + goto leave; + } + copy_and_reverse(q, result_be, result_len); + r = 0; + +leave: + gcry_mpi_point_release(P); + gcry_mpi_point_release(Q); + gcry_ctx_release(ctx); + gcry_mpi_release(mpi); + /* XXX erase priv_be and result_be */ + return r; +} + +int +crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n, + const unsigned char *p) +{ + unsigned char p_be[32]; + gcry_mpi_t mpi_p = NULL; + + copy_and_reverse(p_be, p, 32); + /* Clear unused bit. */ + p_be[0] &= 0x7f; + gcry_mpi_scan(&mpi_p, GCRYMPI_FMT_USG, p_be, 32, NULL); + int r = x25519_mpi(q, n, mpi_p); + gcry_mpi_release(mpi_p); + return r; +} + +int +crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n) +{ + gcry_mpi_t mpi_basepoint_x = gcry_mpi_set_ui(NULL, 9); + int r = x25519_mpi(q, n, mpi_basepoint_x); + gcry_mpi_release(mpi_basepoint_x); + return r; +} +#endif /* HAVE_X25519 */ diff --git a/wsutil/curve25519.h b/wsutil/curve25519.h new file mode 100644 index 0000000000..2868df1b63 --- /dev/null +++ b/wsutil/curve25519.h @@ -0,0 +1,41 @@ +/* curve25519.h + * NaCl/Sodium-compatible API for Curve25519 cryptography. + * + * Copyright (c) 2018, Peter Wu + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#ifndef __CURVE25519_H__ +#define __CURVE25519_H__ + +#include "ws_symbol_export.h" +#include "wsgcrypt.h" + +#if GCRYPT_VERSION_NUMBER >= 0x010700 /* 1.7.0 */ +#define HAVE_X25519 +#endif + +#ifdef HAVE_X25519 +/* + * Computes Q = X25519(n, P). In other words, given the secret key n, the public + * key P, compute the shared secret Q. Each key is 32 bytes long. + * Returns 0 on success or -1 on failure. + */ +WS_DLL_PUBLIC +int crypto_scalarmult_curve25519(unsigned char *q, const unsigned char *n, + const unsigned char *p); + +/* + * Computes the Curve25519 32-byte public key Q from the 32-byte secret key n. + * Returns 0 on success or -1 on failure. + */ +WS_DLL_PUBLIC +int crypto_scalarmult_curve25519_base(unsigned char *q, const unsigned char *n); +#endif /* HAVE_X25519 */ + +#endif /* __CURVE25519_H__ */