| /* |
| * |
| * Embedded Linux library |
| * |
| * Copyright (C) 2015 Intel Corporation. All rights reserved. |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2.1 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library; if not, write to the Free Software |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA |
| * |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include <config.h> |
| #endif |
| |
| #define _GNU_SOURCE |
| #include <unistd.h> |
| #include <stdbool.h> |
| #include <stdint.h> |
| #include <errno.h> |
| #include <sys/socket.h> |
| #include <alloca.h> |
| |
| #include "useful.h" |
| #include "cipher.h" |
| #include "private.h" |
| #include "random.h" |
| #include "missing.h" |
| |
| #ifndef HAVE_LINUX_IF_ALG_H |
| #ifndef HAVE_LINUX_TYPES_H |
| typedef uint8_t __u8; |
| typedef uint16_t __u16; |
| typedef uint32_t __u32; |
| #else |
| #include <linux/types.h> |
| #endif |
| |
| #ifndef AF_ALG |
| #define AF_ALG 38 |
| #define PF_ALG AF_ALG |
| #endif |
| |
| struct sockaddr_alg { |
| __u16 salg_family; |
| __u8 salg_type[14]; |
| __u32 salg_feat; |
| __u32 salg_mask; |
| __u8 salg_name[64]; |
| }; |
| |
| struct af_alg_iv { |
| __u32 ivlen; |
| __u8 iv[0]; |
| }; |
| |
| /* Socket options */ |
| #define ALG_SET_KEY 1 |
| #define ALG_SET_IV 2 |
| #define ALG_SET_OP 3 |
| |
| /* Operations */ |
| #define ALG_OP_DECRYPT 0 |
| #define ALG_OP_ENCRYPT 1 |
| #else |
| #include <linux/if_alg.h> |
| #endif |
| |
| #ifndef SOL_ALG |
| #define SOL_ALG 279 |
| #endif |
| |
| #ifndef ALG_SET_AEAD_ASSOCLEN |
| #define ALG_SET_AEAD_ASSOCLEN 4 |
| #endif |
| |
| #ifndef ALG_SET_AEAD_AUTHSIZE |
| #define ALG_SET_AEAD_AUTHSIZE 5 |
| #endif |
| |
| #define is_valid_type(type) ((type) <= L_CIPHER_RC2_CBC) |
| |
| static uint32_t supported_ciphers; |
| static uint32_t supported_aead_ciphers; |
| |
| struct l_cipher { |
| int type; |
| const struct local_impl *local; |
| union { |
| int sk; |
| void *local_data; |
| }; |
| }; |
| |
| struct l_aead_cipher { |
| int type; |
| int sk; |
| }; |
| |
| struct local_impl { |
| void *(*cipher_new)(enum l_cipher_type, |
| const void *key, size_t key_length); |
| void (*cipher_free)(void *data); |
| bool (*set_iv)(void *data, const uint8_t *iv, size_t iv_length); |
| ssize_t (*operate)(void *data, __u32 operation, |
| const struct iovec *in, size_t in_cnt, |
| const struct iovec *out, size_t out_cnt); |
| }; |
| |
| static int create_alg(const char *alg_type, const char *alg_name, |
| const void *key, size_t key_length, size_t tag_length) |
| { |
| struct sockaddr_alg salg; |
| int sk; |
| int ret; |
| |
| sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); |
| if (sk < 0) |
| return -errno; |
| |
| memset(&salg, 0, sizeof(salg)); |
| salg.salg_family = AF_ALG; |
| strcpy((char *) salg.salg_type, alg_type); |
| strcpy((char *) salg.salg_name, alg_name); |
| |
| if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) { |
| close(sk); |
| return -1; |
| } |
| |
| if (setsockopt(sk, SOL_ALG, ALG_SET_KEY, key, key_length) < 0) { |
| close(sk); |
| return -1; |
| } |
| |
| if (tag_length && setsockopt(sk, SOL_ALG, ALG_SET_AEAD_AUTHSIZE, NULL, |
| tag_length)) { |
| close(sk); |
| return -1; |
| } |
| |
| ret = accept4(sk, NULL, 0, SOCK_CLOEXEC); |
| close(sk); |
| |
| return ret; |
| } |
| |
| static const char *cipher_type_to_name(enum l_cipher_type type) |
| { |
| switch (type) { |
| case L_CIPHER_AES: |
| return "ecb(aes)"; |
| case L_CIPHER_AES_CBC: |
| return "cbc(aes)"; |
| case L_CIPHER_AES_CTR: |
| return "ctr(aes)"; |
| case L_CIPHER_ARC4: |
| return NULL; |
| case L_CIPHER_DES: |
| return "ecb(des)"; |
| case L_CIPHER_DES_CBC: |
| return "cbc(des)"; |
| case L_CIPHER_DES3_EDE_CBC: |
| return "cbc(des3_ede)"; |
| case L_CIPHER_RC2_CBC: |
| return NULL; |
| } |
| |
| return NULL; |
| } |
| |
| static const struct local_impl local_arc4; |
| static const struct local_impl local_rc2_cbc; |
| |
| static const struct local_impl *local_impl_ciphers[] = { |
| [L_CIPHER_ARC4] = &local_arc4, |
| [L_CIPHER_RC2_CBC] = &local_rc2_cbc, |
| }; |
| |
| #define HAVE_LOCAL_IMPLEMENTATION(type) \ |
| ((type) < L_ARRAY_SIZE(local_impl_ciphers) && \ |
| local_impl_ciphers[(type)]) |
| |
| LIB_EXPORT struct l_cipher *l_cipher_new(enum l_cipher_type type, |
| const void *key, |
| size_t key_length) |
| { |
| struct l_cipher *cipher; |
| const char *alg_name; |
| |
| if (unlikely(!key)) |
| return NULL; |
| |
| if (!is_valid_type(type)) |
| return NULL; |
| |
| cipher = l_new(struct l_cipher, 1); |
| cipher->type = type; |
| alg_name = cipher_type_to_name(type); |
| |
| if (HAVE_LOCAL_IMPLEMENTATION(type)) { |
| cipher->local = local_impl_ciphers[type]; |
| cipher->local_data = cipher->local->cipher_new(type, |
| key, key_length); |
| |
| if (!cipher->local_data) |
| goto error_free; |
| |
| return cipher; |
| } |
| |
| cipher->sk = create_alg("skcipher", alg_name, key, key_length, 0); |
| if (cipher->sk < 0) |
| goto error_free; |
| |
| return cipher; |
| |
| error_free: |
| l_free(cipher); |
| return NULL; |
| } |
| |
| static const char *aead_cipher_type_to_name(enum l_aead_cipher_type type) |
| { |
| switch (type) { |
| case L_AEAD_CIPHER_AES_CCM: |
| return "ccm(aes)"; |
| case L_AEAD_CIPHER_AES_GCM: |
| return "gcm(aes)"; |
| } |
| |
| return NULL; |
| } |
| |
| LIB_EXPORT struct l_aead_cipher *l_aead_cipher_new(enum l_aead_cipher_type type, |
| const void *key, |
| size_t key_length, |
| size_t tag_length) |
| { |
| struct l_aead_cipher *cipher; |
| const char *alg_name; |
| |
| if (unlikely(!key)) |
| return NULL; |
| |
| if (type != L_AEAD_CIPHER_AES_CCM && type != L_AEAD_CIPHER_AES_GCM) |
| return NULL; |
| |
| cipher = l_new(struct l_aead_cipher, 1); |
| cipher->type = type; |
| alg_name = aead_cipher_type_to_name(type); |
| |
| cipher->sk = create_alg("aead", alg_name, key, key_length, tag_length); |
| if (cipher->sk >= 0) |
| return cipher; |
| |
| l_free(cipher); |
| return NULL; |
| } |
| |
| LIB_EXPORT void l_cipher_free(struct l_cipher *cipher) |
| { |
| if (unlikely(!cipher)) |
| return; |
| |
| if (cipher->local) |
| cipher->local->cipher_free(cipher->local_data); |
| else |
| close(cipher->sk); |
| |
| l_free(cipher); |
| } |
| |
| LIB_EXPORT void l_aead_cipher_free(struct l_aead_cipher *cipher) |
| { |
| if (unlikely(!cipher)) |
| return; |
| |
| close(cipher->sk); |
| |
| l_free(cipher); |
| } |
| |
| static ssize_t operate_cipher(int sk, __u32 operation, |
| const void *in, size_t in_len, |
| const void *ad, size_t ad_len, |
| const void *iv, size_t iv_len, |
| void *out, size_t out_len) |
| { |
| char *c_msg_buf; |
| size_t c_msg_size; |
| struct msghdr msg; |
| struct cmsghdr *c_msg; |
| struct iovec iov[2]; |
| ssize_t result; |
| |
| c_msg_size = CMSG_SPACE(sizeof(operation)); |
| c_msg_size += ad_len ? CMSG_SPACE(sizeof(uint32_t)) : 0; |
| c_msg_size += iv_len ? |
| CMSG_SPACE(sizeof(struct af_alg_iv) + iv_len) : 0; |
| |
| c_msg_buf = alloca(c_msg_size); |
| |
| memset(c_msg_buf, 0, c_msg_size); |
| memset(&msg, 0, sizeof(msg)); |
| |
| msg.msg_iov = iov; |
| |
| msg.msg_control = c_msg_buf; |
| msg.msg_controllen = c_msg_size; |
| |
| c_msg = CMSG_FIRSTHDR(&msg); |
| c_msg->cmsg_level = SOL_ALG; |
| c_msg->cmsg_type = ALG_SET_OP; |
| c_msg->cmsg_len = CMSG_LEN(sizeof(operation)); |
| memcpy(CMSG_DATA(c_msg), &operation, sizeof(operation)); |
| |
| if (ad_len) { |
| uint32_t *ad_data; |
| |
| c_msg = CMSG_NXTHDR(&msg, c_msg); |
| c_msg->cmsg_level = SOL_ALG; |
| c_msg->cmsg_type = ALG_SET_AEAD_ASSOCLEN; |
| c_msg->cmsg_len = CMSG_LEN(sizeof(*ad_data)); |
| ad_data = (void *) CMSG_DATA(c_msg); |
| *ad_data = ad_len; |
| |
| iov[0].iov_base = (void *) ad; |
| iov[0].iov_len = ad_len; |
| iov[1].iov_base = (void *) in; |
| iov[1].iov_len = in_len; |
| msg.msg_iovlen = 2; |
| } else { |
| iov[0].iov_base = (void *) in; |
| iov[0].iov_len = in_len; |
| msg.msg_iovlen = 1; |
| } |
| |
| if (iv_len) { |
| struct af_alg_iv *algiv; |
| |
| c_msg = CMSG_NXTHDR(&msg, c_msg); |
| c_msg->cmsg_level = SOL_ALG; |
| c_msg->cmsg_type = ALG_SET_IV; |
| c_msg->cmsg_len = CMSG_LEN(sizeof(*algiv) + iv_len); |
| |
| algiv = (void *)CMSG_DATA(c_msg); |
| algiv->ivlen = iv_len; |
| memcpy(algiv->iv, iv, iv_len); |
| } |
| |
| result = sendmsg(sk, &msg, 0); |
| if (result < 0) |
| return -errno; |
| |
| if (ad_len) { |
| /* |
| * When AEAD additional data is passed to sendmsg() for |
| * use in computing the tag, those bytes also appear at |
| * the beginning of the encrypt or decrypt results. Rather |
| * than force the caller to pad their result buffer with |
| * the correct number of bytes for the additional data, |
| * the necessary space is allocated here and then the |
| * duplicate AAD is discarded. |
| */ |
| iov[0].iov_base = l_malloc(ad_len); |
| iov[0].iov_len = ad_len; |
| iov[1].iov_base = (void *) out; |
| iov[1].iov_len = out_len; |
| msg.msg_iovlen = 2; |
| |
| msg.msg_control = NULL; |
| msg.msg_controllen = 0; |
| |
| result = recvmsg(sk, &msg, 0); |
| |
| if (result >= (ssize_t) ad_len) |
| result -= ad_len; |
| else if (result > 0) |
| result = 0; |
| |
| l_free(iov[0].iov_base); |
| } else { |
| result = read(sk, out, out_len); |
| } |
| |
| if (result < 0) |
| return -errno; |
| |
| return result; |
| } |
| |
| static ssize_t operate_cipherv(int sk, __u32 operation, |
| const struct iovec *in, size_t in_cnt, |
| const struct iovec *out, size_t out_cnt) |
| { |
| char *c_msg_buf; |
| size_t c_msg_size; |
| struct msghdr msg; |
| struct cmsghdr *c_msg; |
| ssize_t result; |
| |
| c_msg_size = CMSG_SPACE(sizeof(operation)); |
| c_msg_buf = alloca(c_msg_size); |
| |
| memset(c_msg_buf, 0, c_msg_size); |
| memset(&msg, 0, sizeof(msg)); |
| |
| msg.msg_iov = (struct iovec *) in; |
| msg.msg_iovlen = in_cnt; |
| |
| msg.msg_control = c_msg_buf; |
| msg.msg_controllen = c_msg_size; |
| |
| c_msg = CMSG_FIRSTHDR(&msg); |
| c_msg->cmsg_level = SOL_ALG; |
| c_msg->cmsg_type = ALG_SET_OP; |
| c_msg->cmsg_len = CMSG_LEN(sizeof(operation)); |
| memcpy(CMSG_DATA(c_msg), &operation, sizeof(operation)); |
| |
| result = sendmsg(sk, &msg, 0); |
| if (result < 0) |
| return -errno; |
| |
| result = readv(sk, out, out_cnt); |
| |
| if (result < 0) |
| return -errno; |
| |
| return result; |
| } |
| |
| LIB_EXPORT bool l_cipher_encrypt(struct l_cipher *cipher, |
| const void *in, void *out, size_t len) |
| { |
| if (unlikely(!cipher)) |
| return false; |
| |
| if (unlikely(!in) || unlikely(!out)) |
| return false; |
| |
| if (cipher->local) { |
| struct iovec in_iov = { (void *) in, len }; |
| struct iovec out_iov = { out, len }; |
| |
| return cipher->local->operate(cipher->local_data, |
| ALG_OP_ENCRYPT, |
| &in_iov, 1, &out_iov, 1) >= 0; |
| } |
| |
| return operate_cipher(cipher->sk, ALG_OP_ENCRYPT, in, len, |
| NULL, 0, NULL, 0, out, len) >= 0; |
| } |
| |
| LIB_EXPORT bool l_cipher_encryptv(struct l_cipher *cipher, |
| const struct iovec *in, size_t in_cnt, |
| const struct iovec *out, size_t out_cnt) |
| { |
| if (unlikely(!cipher)) |
| return false; |
| |
| if (unlikely(!in) || unlikely(!out)) |
| return false; |
| |
| if (cipher->local) |
| return cipher->local->operate(cipher->local_data, |
| ALG_OP_ENCRYPT, |
| in, in_cnt, out, out_cnt) >= 0; |
| |
| return operate_cipherv(cipher->sk, ALG_OP_ENCRYPT, in, in_cnt, |
| out, out_cnt) >= 0; |
| } |
| |
| LIB_EXPORT bool l_cipher_decrypt(struct l_cipher *cipher, |
| const void *in, void *out, size_t len) |
| { |
| if (unlikely(!cipher)) |
| return false; |
| |
| if (unlikely(!in) || unlikely(!out)) |
| return false; |
| |
| if (cipher->local) { |
| struct iovec in_iov = { (void *) in, len }; |
| struct iovec out_iov = { out, len }; |
| |
| return cipher->local->operate(cipher->local_data, |
| ALG_OP_DECRYPT, |
| &in_iov, 1, &out_iov, 1) >= 0; |
| } |
| |
| return operate_cipher(cipher->sk, ALG_OP_DECRYPT, in, len, |
| NULL, 0, NULL, 0, out, len) >= 0; |
| } |
| |
| LIB_EXPORT bool l_cipher_decryptv(struct l_cipher *cipher, |
| const struct iovec *in, size_t in_cnt, |
| const struct iovec *out, size_t out_cnt) |
| { |
| if (unlikely(!cipher)) |
| return false; |
| |
| if (unlikely(!in) || unlikely(!out)) |
| return false; |
| |
| if (cipher->local) |
| return cipher->local->operate(cipher->local_data, |
| ALG_OP_DECRYPT, |
| in, in_cnt, out, out_cnt) >= 0; |
| |
| return operate_cipherv(cipher->sk, ALG_OP_DECRYPT, in, in_cnt, |
| out, out_cnt) >= 0; |
| } |
| |
| LIB_EXPORT bool l_cipher_set_iv(struct l_cipher *cipher, const uint8_t *iv, |
| size_t iv_length) |
| { |
| char c_msg_buf[CMSG_SPACE(4 + iv_length)]; |
| struct msghdr msg; |
| struct cmsghdr *c_msg; |
| uint32_t len = iv_length; |
| |
| if (unlikely(!cipher)) |
| return false; |
| |
| if (cipher->local) { |
| if (!cipher->local->set_iv) |
| return false; |
| |
| return cipher->local->set_iv(cipher->local_data, iv, iv_length); |
| } |
| |
| memset(&c_msg_buf, 0, sizeof(c_msg_buf)); |
| memset(&msg, 0, sizeof(struct msghdr)); |
| |
| msg.msg_control = c_msg_buf; |
| msg.msg_controllen = sizeof(c_msg_buf); |
| |
| c_msg = CMSG_FIRSTHDR(&msg); |
| c_msg->cmsg_level = SOL_ALG; |
| c_msg->cmsg_type = ALG_SET_IV; |
| c_msg->cmsg_len = CMSG_LEN(4 + iv_length); |
| memcpy(CMSG_DATA(c_msg) + 0, &len, 4); |
| memcpy(CMSG_DATA(c_msg) + 4, iv, iv_length); |
| |
| msg.msg_iov = NULL; |
| msg.msg_iovlen = 0; |
| |
| if (sendmsg(cipher->sk, &msg, MSG_MORE) < 0) |
| return false; |
| |
| return true; |
| } |
| |
| #define CCM_IV_SIZE 16 |
| |
| static size_t l_aead_cipher_get_ivlen(struct l_aead_cipher *cipher) |
| { |
| switch (cipher->type) { |
| case L_AEAD_CIPHER_AES_CCM: |
| return CCM_IV_SIZE; |
| case L_AEAD_CIPHER_AES_GCM: |
| return 12; |
| } |
| |
| return 0; |
| } |
| |
| /* RFC3610 Section 2.3 */ |
| static ssize_t build_ccm_iv(const void *nonce, uint8_t nonce_len, |
| uint8_t (*iv)[CCM_IV_SIZE]) |
| { |
| const size_t iv_overhead = 2; |
| int lprime = 15 - nonce_len - 1; |
| |
| if (unlikely(nonce_len + iv_overhead > CCM_IV_SIZE || lprime > 7)) |
| return -EINVAL; |
| |
| (*iv)[0] = lprime; |
| memcpy(*iv + 1, nonce, nonce_len); |
| memset(*iv + 1 + nonce_len, 0, lprime + 1); |
| |
| return CCM_IV_SIZE; |
| } |
| |
| LIB_EXPORT bool l_aead_cipher_encrypt(struct l_aead_cipher *cipher, |
| const void *in, size_t in_len, |
| const void *ad, size_t ad_len, |
| const void *nonce, size_t nonce_len, |
| void *out, size_t out_len) |
| { |
| uint8_t ccm_iv[CCM_IV_SIZE]; |
| const uint8_t *iv; |
| ssize_t iv_len; |
| |
| if (unlikely(!cipher)) |
| return false; |
| |
| if (unlikely(!in && !ad) || unlikely(!out)) |
| return false; |
| |
| if (unlikely(!in && in_len) || unlikely(!ad && ad_len)) |
| return false; |
| |
| if (cipher->type == L_AEAD_CIPHER_AES_CCM) { |
| iv_len = build_ccm_iv(nonce, nonce_len, &ccm_iv); |
| if (unlikely(iv_len < 0)) |
| return false; |
| |
| iv = ccm_iv; |
| } else { |
| if (unlikely(nonce_len != l_aead_cipher_get_ivlen(cipher))) |
| return false; |
| |
| iv = nonce; |
| iv_len = nonce_len; |
| } |
| |
| return operate_cipher(cipher->sk, ALG_OP_ENCRYPT, in, in_len, |
| ad, ad_len, iv, iv_len, out, out_len) == |
| (ssize_t)out_len; |
| } |
| |
| LIB_EXPORT bool l_aead_cipher_decrypt(struct l_aead_cipher *cipher, |
| const void *in, size_t in_len, |
| const void *ad, size_t ad_len, |
| const void *nonce, size_t nonce_len, |
| void *out, size_t out_len) |
| { |
| uint8_t ccm_iv[CCM_IV_SIZE]; |
| const uint8_t *iv; |
| ssize_t iv_len; |
| |
| if (unlikely(!cipher)) |
| return false; |
| |
| if (unlikely(!in) || unlikely(!out)) |
| return false; |
| |
| if (cipher->type == L_AEAD_CIPHER_AES_CCM) { |
| iv_len = build_ccm_iv(nonce, nonce_len, &ccm_iv); |
| if (unlikely(iv_len < 0)) |
| return false; |
| |
| iv = ccm_iv; |
| } else { |
| if (unlikely(nonce_len != l_aead_cipher_get_ivlen(cipher))) |
| return false; |
| |
| iv = nonce; |
| iv_len = nonce_len; |
| } |
| |
| return operate_cipher(cipher->sk, ALG_OP_DECRYPT, in, in_len, |
| ad, ad_len, iv, iv_len, out, out_len) == |
| (ssize_t)out_len; |
| } |
| |
| static void init_supported() |
| { |
| static bool initialized = false; |
| struct sockaddr_alg salg; |
| int sk; |
| enum l_cipher_type c; |
| enum l_aead_cipher_type a; |
| |
| if (likely(initialized)) |
| return; |
| |
| initialized = true; |
| |
| for (c = 0; c < L_ARRAY_SIZE(local_impl_ciphers); c++) |
| if (HAVE_LOCAL_IMPLEMENTATION(c)) |
| supported_ciphers |= 1 << c; |
| |
| sk = socket(PF_ALG, SOCK_SEQPACKET | SOCK_CLOEXEC, 0); |
| if (sk < 0) |
| return; |
| |
| memset(&salg, 0, sizeof(salg)); |
| salg.salg_family = AF_ALG; |
| strcpy((char *) salg.salg_type, "skcipher"); |
| |
| for (c = L_CIPHER_AES; c <= L_CIPHER_DES3_EDE_CBC; c++) { |
| const char *name = cipher_type_to_name(c); |
| |
| if (!name) |
| continue; |
| strcpy((char *) salg.salg_name, name); |
| |
| if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) |
| continue; |
| |
| supported_ciphers |= 1 << c; |
| } |
| |
| strcpy((char *) salg.salg_type, "aead"); |
| |
| for (a = L_AEAD_CIPHER_AES_CCM; a <= L_AEAD_CIPHER_AES_GCM; a++) { |
| strcpy((char *) salg.salg_name, aead_cipher_type_to_name(a)); |
| |
| if (bind(sk, (struct sockaddr *) &salg, sizeof(salg)) < 0) |
| continue; |
| |
| supported_aead_ciphers |= 1 << a; |
| } |
| |
| close(sk); |
| } |
| |
| LIB_EXPORT bool l_cipher_is_supported(enum l_cipher_type type) |
| { |
| if (!is_valid_type(type)) |
| return false; |
| |
| init_supported(); |
| |
| return supported_ciphers & (1 << type); |
| } |
| |
| LIB_EXPORT bool l_aead_cipher_is_supported(enum l_aead_cipher_type type) |
| { |
| if (type != L_AEAD_CIPHER_AES_CCM && type != L_AEAD_CIPHER_AES_GCM) |
| return false; |
| |
| init_supported(); |
| |
| return supported_aead_ciphers & (1 << type); |
| } |
| |
| /* ARC4 implementation copyright (c) 2001 Niels Möller */ |
| |
| #define SWAP(a, b) do { uint8_t _t = a; a = b; b = _t; } while (0) |
| |
| static void arc4_set_key(uint8_t *S, const uint8_t *key, size_t key_length) |
| { |
| unsigned int i; |
| uint8_t j; |
| |
| for (i = 0; i < 256; i++) |
| S[i] = i; |
| |
| for (i = j = 0; i < 256; i++) { |
| j += S[i] + key[i % key_length]; |
| SWAP(S[i], S[j]); |
| } |
| } |
| |
| struct arc4_state { |
| struct arc4_state_ctx { |
| uint8_t S[256]; |
| uint8_t i; |
| uint8_t j; |
| } ctx[2]; |
| }; |
| |
| static void *local_arc4_new(enum l_cipher_type type, |
| const void *key, size_t key_length) |
| { |
| struct arc4_state *s; |
| |
| if (unlikely(key_length == 0 || key_length > 256)) |
| return NULL; |
| |
| s = l_new(struct arc4_state, 1); |
| arc4_set_key(s->ctx[0].S, key, key_length); |
| s->ctx[1] = s->ctx[0]; |
| return s; |
| } |
| |
| static void local_arc4_free(void *data) |
| { |
| explicit_bzero(data, sizeof(struct arc4_state)); |
| l_free(data); |
| } |
| |
| static ssize_t local_arc4_operate(void *data, __u32 operation, |
| const struct iovec *in, size_t in_cnt, |
| const struct iovec *out, size_t out_cnt) |
| { |
| struct arc4_state *s = data; |
| struct iovec cur_in; |
| struct iovec cur_out; |
| struct arc4_state_ctx *ctx = |
| &s->ctx[operation == ALG_OP_ENCRYPT ? 1 : 0]; |
| |
| if (!in_cnt || !out_cnt) |
| return 0; |
| |
| cur_in = *in; |
| cur_out = *out; |
| |
| while (1) { |
| while (!cur_in.iov_len) { |
| cur_in = *in++; |
| |
| if (!--in_cnt) |
| return 0; |
| } |
| |
| while (!cur_out.iov_len) { |
| cur_out = *out++; |
| |
| if (!--out_cnt) |
| return 0; |
| } |
| |
| ctx->j += ctx->S[++ctx->i]; |
| SWAP(ctx->S[ctx->i], ctx->S[ctx->j]); |
| *(uint8_t *) cur_out.iov_base++ = |
| *(uint8_t *) cur_in.iov_base++ ^ |
| ctx->S[(ctx->S[ctx->i] + ctx->S[ctx->j]) & 0xff]; |
| cur_in.iov_len--; |
| cur_out.iov_len--; |
| } |
| } |
| |
| static const struct local_impl local_arc4 = { |
| local_arc4_new, |
| local_arc4_free, |
| NULL, |
| local_arc4_operate, |
| }; |
| |
| struct rc2_state { |
| union { |
| uint16_t xkey[64]; |
| uint8_t xkey8[128]; |
| }; |
| struct rc2_state_ctx { |
| union { |
| uint16_t x[4]; |
| uint64_t x64; |
| }; |
| } ctx[2]; |
| }; |
| |
| /* Simplified from the 1996 public-domain implementation */ |
| static void rc2_keyschedule(struct rc2_state *s, |
| const uint8_t *key, size_t key_len, |
| size_t bits) |
| { |
| static const uint8_t permute[256] = { |
| 217,120,249,196, 25,221,181,237, 40,233,253,121, 74,160,216,157, |
| 198,126, 55,131, 43,118, 83,142, 98, 76,100,136, 68,139,251,162, |
| 23,154, 89,245,135,179, 79, 19, 97, 69,109,141, 9,129,125, 50, |
| 189,143, 64,235,134,183,123, 11,240,149, 33, 34, 92,107, 78,130, |
| 84,214,101,147,206, 96,178, 28,115, 86,192, 20,167,140,241,220, |
| 18,117,202, 31, 59,190,228,209, 66, 61,212, 48,163, 60,182, 38, |
| 111,191, 14,218, 70,105, 7, 87, 39,242, 29,155,188,148, 67, 3, |
| 248, 17,199,246,144,239, 62,231, 6,195,213, 47,200,102, 30,215, |
| 8,232,234,222,128, 82,238,247,132,170,114,172, 53, 77,106, 42, |
| 150, 26,210,113, 90, 21, 73,116, 75,159,208, 94, 4, 24,164,236, |
| 194,224, 65,110, 15, 81,203,204, 36,145,175, 80,161,244,112, 57, |
| 153,124, 58,133, 35,184,180,122,252, 2, 54, 91, 37, 85,151, 49, |
| 45, 93,250,152,227,138,146,174, 5,223, 41, 16,103,108,186,201, |
| 211, 0,230,207,225,158,168, 44, 99, 22, 1, 63, 88,226,137,169, |
| 13, 56, 52, 27,171, 51,255,176,187, 72, 12, 95,185,177,205, 46, |
| 197,243,219, 71,229,165,156,119, 10,166, 32,104,254,127,193,173 |
| }; |
| uint8_t x; |
| unsigned int i; |
| |
| memcpy(&s->xkey8, key, key_len); |
| |
| /* Step 1: expand input key to 128 bytes */ |
| x = s->xkey8[key_len - 1]; |
| |
| for (i = 0; key_len < 128; key_len++, i++) |
| s->xkey8[key_len] = x = permute[(x + s->xkey8[i]) & 255]; |
| |
| /* Step 2: reduce effective key size to "bits" */ |
| key_len = (bits + 7) >> 3; |
| i = 128 - key_len; |
| s->xkey8[i] = x = permute[s->xkey8[i] & (255 >> (7 & -bits))]; |
| |
| while (i--) |
| s->xkey8[i] = x = permute[x ^ s->xkey8[i + key_len]]; |
| |
| /* Step 3: copy to xkey in little-endian order */ |
| for (i = 0; i < 64; i++) |
| s->xkey[i] = L_CPU_TO_LE16(s->xkey[i]); |
| } |
| |
| static uint64_t rc2_operate(struct rc2_state *s, uint64_t in, __u32 operation) |
| { |
| int i; |
| union { |
| uint16_t x16[4]; |
| uint64_t x64; |
| } x; |
| |
| x.x64 = in; |
| |
| if (operation == ALG_OP_ENCRYPT) { |
| const uint16_t *xkey = s->xkey; |
| |
| for (i = 0; i < 16; i++) { |
| x.x16[0] += (x.x16[1] & ~x.x16[3]) + |
| (x.x16[2] & x.x16[3]) + *xkey++; |
| x.x16[0] = (x.x16[0] << 1) | (x.x16[0] >> 15); |
| x.x16[1] += (x.x16[2] & ~x.x16[0]) + |
| (x.x16[3] & x.x16[0]) + *xkey++; |
| x.x16[1] = (x.x16[1] << 2) | (x.x16[1] >> 14); |
| x.x16[2] += (x.x16[3] & ~x.x16[1]) + |
| (x.x16[0] & x.x16[1]) + *xkey++; |
| x.x16[2] = (x.x16[2] << 3) | (x.x16[2] >> 13); |
| x.x16[3] += (x.x16[0] & ~x.x16[2]) + |
| (x.x16[1] & x.x16[2]) + *xkey++; |
| x.x16[3] = (x.x16[3] << 5) | (x.x16[3] >> 11); |
| |
| if (i == 4 || i == 10) { |
| x.x16[0] += s->xkey[x.x16[3] & 63]; |
| x.x16[1] += s->xkey[x.x16[0] & 63]; |
| x.x16[2] += s->xkey[x.x16[1] & 63]; |
| x.x16[3] += s->xkey[x.x16[2] & 63]; |
| } |
| } |
| } else { |
| const uint16_t *xkey = s->xkey + 63; |
| |
| for (i = 0; i < 16; i++) { |
| x.x16[3] = (x.x16[3] << 11) | (x.x16[3] >> 5); |
| x.x16[3] -= (x.x16[0] & ~x.x16[2]) + |
| (x.x16[1] & x.x16[2]) + *xkey--; |
| x.x16[2] = (x.x16[2] << 13) | (x.x16[2] >> 3); |
| x.x16[2] -= (x.x16[3] & ~x.x16[1]) + |
| (x.x16[0] & x.x16[1]) + *xkey--; |
| x.x16[1] = (x.x16[1] << 14) | (x.x16[1] >> 2); |
| x.x16[1] -= (x.x16[2] & ~x.x16[0]) + |
| (x.x16[3] & x.x16[0]) + *xkey--; |
| x.x16[0] = (x.x16[0] << 15) | (x.x16[0] >> 1); |
| x.x16[0] -= (x.x16[1] & ~x.x16[3]) + |
| (x.x16[2] & x.x16[3]) + *xkey--; |
| |
| if (i == 4 || i == 10) { |
| x.x16[3] -= s->xkey[x.x16[2] & 63]; |
| x.x16[2] -= s->xkey[x.x16[1] & 63]; |
| x.x16[1] -= s->xkey[x.x16[0] & 63]; |
| x.x16[0] -= s->xkey[x.x16[3] & 63]; |
| } |
| } |
| } |
| |
| return x.x64; |
| } |
| |
| static void *local_rc2_cbc_new(enum l_cipher_type type, |
| const void *key, size_t key_length) |
| { |
| struct rc2_state *s; |
| |
| if (unlikely(key_length == 0 || key_length > 128)) |
| return NULL; |
| |
| /* |
| * The key length and the effective "strength" bits are separate |
| * parameters but they match in our current use cases. |
| */ |
| s = l_new(struct rc2_state, 1); |
| rc2_keyschedule(s, key, key_length, key_length * 8); |
| return s; |
| } |
| |
| static void local_rc2_cbc_free(void *data) |
| { |
| explicit_bzero(data, sizeof(struct rc2_state)); |
| l_free(data); |
| } |
| |
| static bool local_rc2_cbc_set_iv(void *data, |
| const uint8_t *iv, size_t iv_length) |
| { |
| struct rc2_state *s = data; |
| |
| if (unlikely(iv_length != 8)) |
| return false; |
| |
| s->ctx[0].x[0] = l_get_le16(iv + 0); |
| s->ctx[0].x[1] = l_get_le16(iv + 2); |
| s->ctx[0].x[2] = l_get_le16(iv + 4); |
| s->ctx[0].x[3] = l_get_le16(iv + 6); |
| s->ctx[1].x64 = s->ctx[0].x64; |
| return true; |
| } |
| |
| static ssize_t local_rc2_cbc_operate(void *data, __u32 operation, |
| const struct iovec *in, size_t in_cnt, |
| const struct iovec *out, size_t out_cnt) |
| { |
| struct rc2_state *s = data; |
| struct iovec cur_in = {}; |
| struct iovec cur_out = {}; |
| struct rc2_state_ctx *ctx = |
| &s->ctx[operation == ALG_OP_ENCRYPT ? 1 : 0]; |
| |
| #define CONSUME_IN(bytes, eof_ok) \ |
| cur_in.iov_len -= (bytes); \ |
| while (!cur_in.iov_len) { \ |
| if (!in_cnt) { \ |
| if (eof_ok) \ |
| break; \ |
| else \ |
| return -1; \ |
| } \ |
| \ |
| cur_in = *in++; \ |
| in_cnt--; \ |
| } |
| |
| #define CONSUME_OUT(bytes) \ |
| cur_out.iov_len -= (bytes); \ |
| while (!cur_out.iov_len) { \ |
| if (!out_cnt) \ |
| return 0; \ |
| \ |
| cur_out = *out++; \ |
| out_cnt--; \ |
| } |
| |
| CONSUME_IN(0, true) |
| CONSUME_OUT(0) |
| |
| while (cur_in.iov_len) { |
| union { |
| uint16_t x16[4]; |
| uint64_t x64; |
| } inblk; |
| |
| if (cur_in.iov_len >= 8) { |
| #define CUR_IN16 (*(uint16_t **) &cur_in.iov_base) |
| inblk.x16[0] = l_get_le16(CUR_IN16++); |
| inblk.x16[1] = l_get_le16(CUR_IN16++); |
| inblk.x16[2] = l_get_le16(CUR_IN16++); |
| inblk.x16[3] = l_get_le16(CUR_IN16++); |
| CONSUME_IN(8, true) |
| } else { |
| inblk.x16[0] = *(uint8_t *) cur_in.iov_base++; |
| CONSUME_IN(1, false) |
| inblk.x16[0] |= (*(uint8_t *) cur_in.iov_base++) << 8; |
| CONSUME_IN(1, false) |
| inblk.x16[1] = *(uint8_t *) cur_in.iov_base++; |
| CONSUME_IN(1, false) |
| inblk.x16[1] |= (*(uint8_t *) cur_in.iov_base++) << 8; |
| CONSUME_IN(1, false) |
| inblk.x16[2] = *(uint8_t *) cur_in.iov_base++; |
| CONSUME_IN(1, false) |
| inblk.x16[2] |= (*(uint8_t *) cur_in.iov_base++) << 8; |
| CONSUME_IN(1, false) |
| inblk.x16[3] = *(uint8_t *) cur_in.iov_base++; |
| CONSUME_IN(1, false) |
| inblk.x16[3] |= (*(uint8_t *) cur_in.iov_base++) << 8; |
| CONSUME_IN(1, true) |
| } |
| |
| if (operation == ALG_OP_ENCRYPT) |
| ctx->x64 = rc2_operate(s, inblk.x64 ^ ctx->x64, |
| operation); |
| else |
| ctx->x64 ^= rc2_operate(s, inblk.x64, operation); |
| |
| if (cur_out.iov_len >= 8) { |
| #define CUR_OUT16 (*(uint16_t **) &cur_out.iov_base) |
| l_put_le16(ctx->x[0], CUR_OUT16++); |
| l_put_le16(ctx->x[1], CUR_OUT16++); |
| l_put_le16(ctx->x[2], CUR_OUT16++); |
| l_put_le16(ctx->x[3], CUR_OUT16++); |
| CONSUME_OUT(8) |
| } else { |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[0]; |
| CONSUME_OUT(1) |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[0] >> 8; |
| CONSUME_OUT(1) |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[1]; |
| CONSUME_OUT(1) |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[1] >> 8; |
| CONSUME_OUT(1) |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[2]; |
| CONSUME_OUT(1) |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[2] >> 8; |
| CONSUME_OUT(1) |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[3]; |
| CONSUME_OUT(1) |
| *(uint8_t *) cur_out.iov_base++ = ctx->x[3] >> 8; |
| CONSUME_OUT(1) |
| } |
| |
| /* Save ciphertext as IV for next CBC block */ |
| if (operation == ALG_OP_DECRYPT) |
| ctx->x64 = inblk.x64; |
| |
| inblk.x64 = 0; |
| } |
| |
| return 0; |
| } |
| |
| static const struct local_impl local_rc2_cbc = { |
| local_rc2_cbc_new, |
| local_rc2_cbc_free, |
| local_rc2_cbc_set_iv, |
| local_rc2_cbc_operate, |
| }; |