1 /* 2 * QEMU Crypto af_alg-backend cipher support 3 * 4 * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD. 5 * 6 * Authors: 7 * Longpeng(Mike) <longpeng2@huawei.com> 8 * 9 * This work is licensed under the terms of the GNU GPL, version 2 or 10 * (at your option) any later version. See the COPYING file in the 11 * top-level directory. 12 */ 13 #include "qemu/osdep.h" 14 #include "qemu/sockets.h" 15 #include "qapi/error.h" 16 #include "crypto/cipher.h" 17 #include "cipherpriv.h" 18 19 20 static char * 21 qcrypto_afalg_cipher_format_name(QCryptoCipherAlgo alg, 22 QCryptoCipherMode mode, 23 Error **errp) 24 { 25 char *name; 26 const char *alg_name; 27 const char *mode_name; 28 29 switch (alg) { 30 case QCRYPTO_CIPHER_ALGO_AES_128: 31 case QCRYPTO_CIPHER_ALGO_AES_192: 32 case QCRYPTO_CIPHER_ALGO_AES_256: 33 alg_name = "aes"; 34 break; 35 case QCRYPTO_CIPHER_ALGO_CAST5_128: 36 alg_name = "cast5"; 37 break; 38 case QCRYPTO_CIPHER_ALGO_SERPENT_128: 39 case QCRYPTO_CIPHER_ALGO_SERPENT_192: 40 case QCRYPTO_CIPHER_ALGO_SERPENT_256: 41 alg_name = "serpent"; 42 break; 43 case QCRYPTO_CIPHER_ALGO_TWOFISH_128: 44 case QCRYPTO_CIPHER_ALGO_TWOFISH_192: 45 case QCRYPTO_CIPHER_ALGO_TWOFISH_256: 46 alg_name = "twofish"; 47 break; 48 49 default: 50 error_setg(errp, "Unsupported cipher algorithm %d", alg); 51 return NULL; 52 } 53 54 mode_name = QCryptoCipherMode_str(mode); 55 name = g_strdup_printf("%s(%s)", mode_name, alg_name); 56 57 return name; 58 } 59 60 static const struct QCryptoCipherDriver qcrypto_cipher_afalg_driver; 61 62 QCryptoCipher * 63 qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgo alg, 64 QCryptoCipherMode mode, 65 const uint8_t *key, 66 size_t nkey, Error **errp) 67 { 68 QCryptoAFAlgo *afalg; 69 size_t expect_niv; 70 char *name; 71 72 name = qcrypto_afalg_cipher_format_name(alg, mode, errp); 73 if (!name) { 74 return NULL; 75 } 76 77 afalg = qcrypto_afalg_comm_alloc(AFALG_TYPE_CIPHER, name, errp); 78 if (!afalg) { 79 g_free(name); 80 return NULL; 81 } 82 83 g_free(name); 84 85 /* setkey */ 86 if (setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, key, 87 nkey) != 0) { 88 error_setg_errno(errp, errno, "Set key failed"); 89 qcrypto_afalg_comm_free(afalg); 90 return NULL; 91 } 92 93 /* prepare msg header */ 94 afalg->msg = g_new0(struct msghdr, 1); 95 afalg->msg->msg_controllen += CMSG_SPACE(ALG_OPTYPE_LEN); 96 expect_niv = qcrypto_cipher_get_iv_len(alg, mode); 97 if (expect_niv) { 98 afalg->msg->msg_controllen += CMSG_SPACE(ALG_MSGIV_LEN(expect_niv)); 99 } 100 afalg->msg->msg_control = g_new0(uint8_t, afalg->msg->msg_controllen); 101 102 /* We use 1st msghdr for crypto-info and 2nd msghdr for IV-info */ 103 afalg->cmsg = CMSG_FIRSTHDR(afalg->msg); 104 afalg->cmsg->cmsg_type = ALG_SET_OP; 105 afalg->cmsg->cmsg_len = CMSG_SPACE(ALG_OPTYPE_LEN); 106 if (expect_niv) { 107 afalg->cmsg = CMSG_NXTHDR(afalg->msg, afalg->cmsg); 108 afalg->cmsg->cmsg_type = ALG_SET_IV; 109 afalg->cmsg->cmsg_len = CMSG_SPACE(ALG_MSGIV_LEN(expect_niv)); 110 } 111 afalg->cmsg = CMSG_FIRSTHDR(afalg->msg); 112 113 afalg->base.driver = &qcrypto_cipher_afalg_driver; 114 return &afalg->base; 115 } 116 117 static int 118 qcrypto_afalg_cipher_setiv(QCryptoCipher *cipher, 119 const uint8_t *iv, 120 size_t niv, Error **errp) 121 { 122 QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); 123 struct af_alg_iv *alg_iv; 124 size_t expect_niv; 125 126 expect_niv = qcrypto_cipher_get_iv_len(cipher->alg, cipher->mode); 127 if (niv != expect_niv) { 128 error_setg(errp, "Set IV len(%zu) not match expected(%zu)", 129 niv, expect_niv); 130 return -1; 131 } 132 133 /* move ->cmsg to next msghdr, for IV-info */ 134 afalg->cmsg = CMSG_NXTHDR(afalg->msg, afalg->cmsg); 135 136 /* build setiv msg */ 137 afalg->cmsg->cmsg_level = SOL_ALG; 138 alg_iv = (struct af_alg_iv *)CMSG_DATA(afalg->cmsg); 139 alg_iv->ivlen = niv; 140 memcpy(alg_iv->iv, iv, niv); 141 142 return 0; 143 } 144 145 static int 146 qcrypto_afalg_cipher_op(QCryptoAFAlgo *afalg, 147 const void *in, void *out, 148 size_t len, bool do_encrypt, 149 Error **errp) 150 { 151 uint32_t *type = NULL; 152 struct iovec iov; 153 size_t ret, rlen, done = 0; 154 uint32_t origin_controllen; 155 156 origin_controllen = afalg->msg->msg_controllen; 157 /* movev ->cmsg to first header, for crypto-info */ 158 afalg->cmsg = CMSG_FIRSTHDR(afalg->msg); 159 160 /* build encrypt msg */ 161 afalg->cmsg->cmsg_level = SOL_ALG; 162 afalg->msg->msg_iov = &iov; 163 afalg->msg->msg_iovlen = 1; 164 type = (uint32_t *)CMSG_DATA(afalg->cmsg); 165 if (do_encrypt) { 166 *type = ALG_OP_ENCRYPT; 167 } else { 168 *type = ALG_OP_DECRYPT; 169 } 170 171 do { 172 iov.iov_base = (void *)in + done; 173 iov.iov_len = len - done; 174 175 /* send info to AF_ALG core */ 176 ret = sendmsg(afalg->opfd, afalg->msg, 0); 177 if (ret == -1) { 178 error_setg_errno(errp, errno, "Send data to AF_ALG core failed"); 179 return -1; 180 } 181 182 /* encrypto && get result */ 183 rlen = read(afalg->opfd, out, ret); 184 if (rlen == -1) { 185 error_setg_errno(errp, errno, "Get result from AF_ALG core failed"); 186 return -1; 187 } 188 assert(rlen == ret); 189 190 /* do not update IV for following chunks */ 191 afalg->msg->msg_controllen = 0; 192 done += ret; 193 } while (done < len); 194 195 afalg->msg->msg_controllen = origin_controllen; 196 197 return 0; 198 } 199 200 static int 201 qcrypto_afalg_cipher_encrypt(QCryptoCipher *cipher, 202 const void *in, void *out, 203 size_t len, Error **errp) 204 { 205 QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); 206 207 return qcrypto_afalg_cipher_op(afalg, in, out, len, true, errp); 208 } 209 210 static int 211 qcrypto_afalg_cipher_decrypt(QCryptoCipher *cipher, 212 const void *in, void *out, 213 size_t len, Error **errp) 214 { 215 QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); 216 217 return qcrypto_afalg_cipher_op(afalg, in, out, len, false, errp); 218 } 219 220 static void qcrypto_afalg_comm_ctx_free(QCryptoCipher *cipher) 221 { 222 QCryptoAFAlgo *afalg = container_of(cipher, QCryptoAFAlgo, base); 223 224 qcrypto_afalg_comm_free(afalg); 225 } 226 227 static const struct QCryptoCipherDriver qcrypto_cipher_afalg_driver = { 228 .cipher_encrypt = qcrypto_afalg_cipher_encrypt, 229 .cipher_decrypt = qcrypto_afalg_cipher_decrypt, 230 .cipher_setiv = qcrypto_afalg_cipher_setiv, 231 .cipher_free = qcrypto_afalg_comm_ctx_free, 232 }; 233