xref: /openbmc/qemu/crypto/cipher-afalg.c (revision e7bbc9b1)
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 "qemu-common.h"
16 #include "qapi/error.h"
17 #include "crypto/cipher.h"
18 #include "cipherpriv.h"
19 
20 
21 static char *
22 qcrypto_afalg_cipher_format_name(QCryptoCipherAlgorithm alg,
23                                  QCryptoCipherMode mode,
24                                  Error **errp)
25 {
26     char *name;
27     const char *alg_name;
28     const char *mode_name;
29 
30     switch (alg) {
31     case QCRYPTO_CIPHER_ALG_AES_128:
32     case QCRYPTO_CIPHER_ALG_AES_192:
33     case QCRYPTO_CIPHER_ALG_AES_256:
34         alg_name = "aes";
35         break;
36     case QCRYPTO_CIPHER_ALG_CAST5_128:
37         alg_name = "cast5";
38         break;
39     case QCRYPTO_CIPHER_ALG_SERPENT_128:
40     case QCRYPTO_CIPHER_ALG_SERPENT_192:
41     case QCRYPTO_CIPHER_ALG_SERPENT_256:
42         alg_name = "serpent";
43         break;
44     case QCRYPTO_CIPHER_ALG_TWOFISH_128:
45     case QCRYPTO_CIPHER_ALG_TWOFISH_192:
46     case QCRYPTO_CIPHER_ALG_TWOFISH_256:
47         alg_name = "twofish";
48         break;
49 
50     default:
51         error_setg(errp, "Unsupported cipher algorithm %d", alg);
52         return NULL;
53     }
54 
55     mode_name = QCryptoCipherMode_str(mode);
56     name = g_strdup_printf("%s(%s)", mode_name, alg_name);
57 
58     return name;
59 }
60 
61 QCryptoAFAlg *
62 qcrypto_afalg_cipher_ctx_new(QCryptoCipherAlgorithm alg,
63                              QCryptoCipherMode mode,
64                              const uint8_t *key,
65                              size_t nkey, Error **errp)
66 {
67     QCryptoAFAlg *afalg;
68     size_t expect_niv;
69     char *name;
70 
71     name = qcrypto_afalg_cipher_format_name(alg, mode, errp);
72     if (!name) {
73         return NULL;
74     }
75 
76     afalg = qcrypto_afalg_comm_alloc(AFALG_TYPE_CIPHER, name, errp);
77     if (!afalg) {
78         g_free(name);
79         return NULL;
80     }
81 
82     g_free(name);
83 
84     /* setkey */
85     if (qemu_setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY, key,
86                         nkey) != 0) {
87         error_setg_errno(errp, errno, "Set key failed");
88         qcrypto_afalg_comm_free(afalg);
89         return NULL;
90     }
91 
92     /* prepare msg header */
93     afalg->msg = g_new0(struct msghdr, 1);
94     afalg->msg->msg_controllen += CMSG_SPACE(ALG_OPTYPE_LEN);
95     expect_niv = qcrypto_cipher_get_iv_len(alg, mode);
96     if (expect_niv) {
97         afalg->msg->msg_controllen += CMSG_SPACE(ALG_MSGIV_LEN(expect_niv));
98     }
99     afalg->msg->msg_control = g_new0(uint8_t, afalg->msg->msg_controllen);
100 
101     /* We use 1st msghdr for crypto-info and 2nd msghdr for IV-info */
102     afalg->cmsg = CMSG_FIRSTHDR(afalg->msg);
103     afalg->cmsg->cmsg_type = ALG_SET_OP;
104     afalg->cmsg->cmsg_len = CMSG_SPACE(ALG_OPTYPE_LEN);
105     if (expect_niv) {
106         afalg->cmsg = CMSG_NXTHDR(afalg->msg, afalg->cmsg);
107         afalg->cmsg->cmsg_type = ALG_SET_IV;
108         afalg->cmsg->cmsg_len = CMSG_SPACE(ALG_MSGIV_LEN(expect_niv));
109     }
110     afalg->cmsg = CMSG_FIRSTHDR(afalg->msg);
111 
112     return afalg;
113 }
114 
115 static int
116 qcrypto_afalg_cipher_setiv(QCryptoCipher *cipher,
117                            const uint8_t *iv,
118                            size_t niv, Error **errp)
119 {
120     struct af_alg_iv *alg_iv;
121     size_t expect_niv;
122     QCryptoAFAlg *afalg = cipher->opaque;
123 
124     expect_niv = qcrypto_cipher_get_iv_len(cipher->alg, cipher->mode);
125     if (niv != expect_niv) {
126         error_setg(errp, "Set IV len(%zu) not match expected(%zu)",
127                    niv, expect_niv);
128         return -1;
129     }
130 
131     /* move ->cmsg to next msghdr, for IV-info */
132     afalg->cmsg = CMSG_NXTHDR(afalg->msg, afalg->cmsg);
133 
134     /* build setiv msg */
135     afalg->cmsg->cmsg_level = SOL_ALG;
136     alg_iv = (struct af_alg_iv *)CMSG_DATA(afalg->cmsg);
137     alg_iv->ivlen = niv;
138     memcpy(alg_iv->iv, iv, niv);
139 
140     return 0;
141 }
142 
143 static int
144 qcrypto_afalg_cipher_op(QCryptoAFAlg *afalg,
145                         const void *in, void *out,
146                         size_t len, bool do_encrypt,
147                         Error **errp)
148 {
149     uint32_t *type = NULL;
150     struct iovec iov;
151     size_t ret, rlen, done = 0;
152     uint32_t origin_controllen;
153 
154     origin_controllen = afalg->msg->msg_controllen;
155     /* movev ->cmsg to first header, for crypto-info */
156     afalg->cmsg = CMSG_FIRSTHDR(afalg->msg);
157 
158     /* build encrypt msg */
159     afalg->cmsg->cmsg_level = SOL_ALG;
160     afalg->msg->msg_iov = &iov;
161     afalg->msg->msg_iovlen = 1;
162     type = (uint32_t *)CMSG_DATA(afalg->cmsg);
163     if (do_encrypt) {
164         *type = ALG_OP_ENCRYPT;
165     } else {
166         *type = ALG_OP_DECRYPT;
167     }
168 
169     do {
170         iov.iov_base = (void *)in + done;
171         iov.iov_len = len - done;
172 
173         /* send info to AF_ALG core */
174         ret = sendmsg(afalg->opfd, afalg->msg, 0);
175         if (ret == -1) {
176             error_setg_errno(errp, errno, "Send data to AF_ALG core failed");
177             return -1;
178         }
179 
180         /* encrypto && get result */
181         rlen = read(afalg->opfd, out, ret);
182         if (rlen == -1) {
183             error_setg_errno(errp, errno, "Get result from AF_ALG core failed");
184             return -1;
185         }
186         assert(rlen == ret);
187 
188         /* do not update IV for following chunks */
189         afalg->msg->msg_controllen = 0;
190         done += ret;
191     } while (done < len);
192 
193     afalg->msg->msg_controllen = origin_controllen;
194 
195     return 0;
196 }
197 
198 static int
199 qcrypto_afalg_cipher_encrypt(QCryptoCipher *cipher,
200                              const void *in, void *out,
201                              size_t len, Error **errp)
202 {
203     return qcrypto_afalg_cipher_op(cipher->opaque, in, out,
204                                    len, true, errp);
205 }
206 
207 static int
208 qcrypto_afalg_cipher_decrypt(QCryptoCipher *cipher,
209                              const void *in, void *out,
210                              size_t len, Error **errp)
211 {
212     return qcrypto_afalg_cipher_op(cipher->opaque, in, out,
213                                    len, false, errp);
214 }
215 
216 static void qcrypto_afalg_comm_ctx_free(QCryptoCipher *cipher)
217 {
218     qcrypto_afalg_comm_free(cipher->opaque);
219 }
220 
221 struct QCryptoCipherDriver qcrypto_cipher_afalg_driver = {
222     .cipher_encrypt = qcrypto_afalg_cipher_encrypt,
223     .cipher_decrypt = qcrypto_afalg_cipher_decrypt,
224     .cipher_setiv = qcrypto_afalg_cipher_setiv,
225     .cipher_free = qcrypto_afalg_comm_ctx_free,
226 };
227