| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114 |
- /*
- *
- * 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,
- };
|