From 1db8a85a60c645232eb5bba1ec0cd0a2927ccd16 Mon Sep 17 00:00:00 2001 From: Ard Biesheuvel Date: Mon, 3 Aug 2020 12:45:09 +0200 Subject: [PATCH] crypto: incorporate C implementation of ARC4 Incorporate the LGPL v2.1 licensed implementation of ARC4, taken from the Nettle project (https://git.lysator.liu.se/nettle/nettle.git, commit 3e7a480a1e351884), and tweak it a bit so we don't have to operate on a skip buffer to fast forward the stream cipher, but can simply invoke it with NULL dst or src arguments to achieve the same. This removes the dependency [via libell] on the OS's implementation of ecb(arc4), which may be going away, and which is not usually accelerated in the first place. --- src/crypto.c | 88 +++++++++++++++++++++++++++++------------------ src/main.c | 8 ----- unit/test-eapol.c | 3 +- 3 files changed, 56 insertions(+), 43 deletions(-) diff --git a/src/crypto.c b/src/crypto.c index 696b5990..f5f8e24d 100644 --- a/src/crypto.c +++ b/src/crypto.c @@ -18,6 +18,8 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * + * (contains ARC4 implementation copyright (c) 2001 Niels Möller) + * */ #ifdef HAVE_CONFIG_H @@ -34,6 +36,16 @@ #include "src/missing.h" #include "src/crypto.h" +#define ARC4_MIN_KEY_SIZE 1 +#define ARC4_MAX_KEY_SIZE 256 +#define ARC4_KEY_SIZE 16 + +struct arc4_ctx { + uint8_t S[256]; + uint8_t i; + uint8_t j; +}; + /* RFC 3526, Section 2 */ const unsigned char crypto_dh5_prime[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xc9, 0x0f, 0xda, 0xa2, @@ -415,44 +427,54 @@ free_ctr: return false; } +#define SWAP(a,b) do { int _t = a; a = b; b = _t; } while (0) + +static void arc4_set_key(struct arc4_ctx *ctx, unsigned length, + const uint8_t *key) +{ + unsigned int i, j, k; + + /* Initialize context */ + for (i = 0; i < 256; i++) + ctx->S[i] = i; + + for (i = j = k = 0; i < 256; i++) { + j += ctx->S[i] + key[k]; j &= 0xff; + SWAP(ctx->S[i], ctx->S[j]); + /* Repeat key as needed */ + k = (k + 1) % length; + } + ctx->i = ctx->j = 0; +} + +static void arc4_crypt(struct arc4_ctx *ctx, unsigned length, uint8_t *dst, + const uint8_t *src) +{ + uint8_t i, j; + + i = ctx->i; j = ctx->j; + while (length--) { + i++; i &= 0xff; + j += ctx->S[i]; j &= 0xff; + SWAP(ctx->S[i], ctx->S[j]); + if (!dst || !src) + continue; + *dst++ = *src++ ^ ctx->S[ (ctx->S[i] + ctx->S[j]) & 0xff ]; + } + ctx->i = i; ctx->j = j; +} + bool arc4_skip(const uint8_t *key, size_t key_len, size_t skip, const uint8_t *in, size_t len, uint8_t *out) { - char skip_buf[1024]; - struct l_cipher *cipher; - struct iovec in_vec[2]; - struct iovec out_vec[2]; - bool r; + struct arc4_ctx cipher; - cipher = l_cipher_new(L_CIPHER_ARC4, key, key_len); - if (!cipher) - return false; + arc4_set_key(&cipher, key_len, key); + arc4_crypt(&cipher, skip, NULL, NULL); + arc4_crypt(&cipher, len, out, in); + explicit_bzero(&cipher, sizeof(cipher)); - /* This is not strictly necessary, but keeps valgrind happy */ - memset(skip_buf, 0, sizeof(skip_buf)); - - while (skip > sizeof(skip_buf)) { - size_t to_skip = - skip > sizeof(skip_buf) ? sizeof(skip_buf) : skip; - - l_cipher_decrypt(cipher, skip_buf, skip_buf, to_skip); - skip -= to_skip; - } - - in_vec[0].iov_base = skip_buf; - in_vec[0].iov_len = skip; - in_vec[1].iov_base = (void *) in; - in_vec[1].iov_len = len; - - out_vec[0].iov_base = skip_buf; - out_vec[0].iov_len = skip; - out_vec[1].iov_base = out; - out_vec[1].iov_len = len; - - r = l_cipher_decryptv(cipher, in_vec, 2, out_vec, 2); - l_cipher_free(cipher); - - return r; + return true; } /* 802.11, Section 11.6.2, Table 11-4 */ diff --git a/src/main.c b/src/main.c index 7c087463..3216f508 100644 --- a/src/main.c +++ b/src/main.c @@ -277,14 +277,6 @@ static int check_crypto() ADD_OPTIONAL("CONFIG_CRYPTO_SHA512_SSSE3"); } - if (!l_cipher_is_supported(L_CIPHER_ARC4)) { - r = -ENOTSUP; - l_error("RC4 support not found"); - ADD_MISSING("CONFIG_CRYPTO_USER_API_SKCIPHER"); - ADD_MISSING("CONFIG_CRYPTO_ARC4"); - ADD_MISSING("CONFIG_CRYPTO_ECB"); - } - if (!l_cipher_is_supported(L_CIPHER_DES) || !l_cipher_is_supported(L_CIPHER_DES3_EDE_CBC)) { r = -ENOTSUP; diff --git a/unit/test-eapol.c b/unit/test-eapol.c index ac94522b..f6af6f06 100644 --- a/unit/test-eapol.c +++ b/unit/test-eapol.c @@ -3600,8 +3600,7 @@ int main(int argc, char *argv[]) l_test_add("/EAPoL Key/Calculate MIC Test 1", eapol_calculate_mic_test, &eapol_calculate_mic_test_1); - if (!l_cipher_is_supported(L_CIPHER_AES) || - !l_cipher_is_supported(L_CIPHER_ARC4)) + if (!l_cipher_is_supported(L_CIPHER_AES)) goto done; l_test_add("EAPoL/WPA2 4-Way Handshake",