xref: /qemu/crypto/hash-afalg.c (revision 90c3dc60735a3ccd6a3d4235470bdb71b6d7eb32)
1  /*
2   * QEMU Crypto af_alg-backend hash/hmac support
3   *
4   * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates
5   * Copyright (c) 2017 HUAWEI TECHNOLOGIES CO., LTD.
6   *
7   * Authors:
8   *    Longpeng(Mike) <longpeng2@huawei.com>
9   *
10   * This work is licensed under the terms of the GNU GPL, version 2 or
11   * (at your option) any later version.  See the COPYING file in the
12   * top-level directory.
13   */
14  #include "qemu/osdep.h"
15  #include "qemu/iov.h"
16  #include "qemu/sockets.h"
17  #include "qapi/error.h"
18  #include "crypto/hash.h"
19  #include "crypto/hmac.h"
20  #include "hashpriv.h"
21  #include "hmacpriv.h"
22  
23  static char *
24  qcrypto_afalg_hash_format_name(QCryptoHashAlgo alg,
25                                 bool is_hmac,
26                                 Error **errp)
27  {
28      char *name;
29      const char *alg_name;
30  
31      switch (alg) {
32      case QCRYPTO_HASH_ALGO_MD5:
33          alg_name = "md5";
34          break;
35      case QCRYPTO_HASH_ALGO_SHA1:
36          alg_name = "sha1";
37          break;
38      case QCRYPTO_HASH_ALGO_SHA224:
39          alg_name = "sha224";
40          break;
41      case QCRYPTO_HASH_ALGO_SHA256:
42          alg_name = "sha256";
43          break;
44      case QCRYPTO_HASH_ALGO_SHA384:
45          alg_name = "sha384";
46          break;
47      case QCRYPTO_HASH_ALGO_SHA512:
48          alg_name = "sha512";
49          break;
50      case QCRYPTO_HASH_ALGO_RIPEMD160:
51          alg_name = "rmd160";
52          break;
53  
54      default:
55          error_setg(errp, "Unsupported hash algorithm %d", alg);
56          return NULL;
57      }
58  
59      if (is_hmac) {
60          name = g_strdup_printf("hmac(%s)", alg_name);
61      } else {
62          name = g_strdup_printf("%s", alg_name);
63      }
64  
65      return name;
66  }
67  
68  static QCryptoAFAlgo *
69  qcrypto_afalg_hash_hmac_ctx_new(QCryptoHashAlgo alg,
70                                  const uint8_t *key, size_t nkey,
71                                  bool is_hmac, Error **errp)
72  {
73      QCryptoAFAlgo *afalg;
74      char *name;
75  
76      name = qcrypto_afalg_hash_format_name(alg, is_hmac, errp);
77      if (!name) {
78          return NULL;
79      }
80  
81      afalg = qcrypto_afalg_comm_alloc(AFALG_TYPE_HASH, name, errp);
82      if (!afalg) {
83          g_free(name);
84          return NULL;
85      }
86  
87      g_free(name);
88  
89      /* HMAC needs setkey */
90      if (is_hmac) {
91          if (setsockopt(afalg->tfmfd, SOL_ALG, ALG_SET_KEY,
92                         key, nkey) != 0) {
93              error_setg_errno(errp, errno, "Set hmac key failed");
94              qcrypto_afalg_comm_free(afalg);
95              return NULL;
96          }
97      }
98  
99      return afalg;
100  }
101  
102  static QCryptoAFAlgo *
103  qcrypto_afalg_hash_ctx_new(QCryptoHashAlgo alg,
104                             Error **errp)
105  {
106      return qcrypto_afalg_hash_hmac_ctx_new(alg, NULL, 0, false, errp);
107  }
108  
109  QCryptoAFAlgo *
110  qcrypto_afalg_hmac_ctx_new(QCryptoHashAlgo alg,
111                             const uint8_t *key, size_t nkey,
112                             Error **errp)
113  {
114      return qcrypto_afalg_hash_hmac_ctx_new(alg, key, nkey, true, errp);
115  }
116  
117  static
118  QCryptoHash *qcrypto_afalg_hash_new(QCryptoHashAlgo alg, Error **errp)
119  {
120      /* Check if hash algorithm is supported */
121      char *alg_name = qcrypto_afalg_hash_format_name(alg, false, NULL);
122      QCryptoHash *hash;
123  
124      if (alg_name == NULL) {
125          error_setg(errp, "Unknown hash algorithm %d", alg);
126          return NULL;
127      }
128  
129      g_free(alg_name);
130  
131      hash = g_new(QCryptoHash, 1);
132      hash->alg = alg;
133      hash->opaque = qcrypto_afalg_hash_ctx_new(alg, errp);
134      if (!hash->opaque) {
135          free(hash);
136          return NULL;
137      }
138  
139      return hash;
140  }
141  
142  static
143  void qcrypto_afalg_hash_free(QCryptoHash *hash)
144  {
145      QCryptoAFAlg *ctx = hash->opaque;
146  
147      if (ctx) {
148          qcrypto_afalg_comm_free(ctx);
149      }
150  
151      g_free(hash);
152  }
153  
154  /**
155   * Send data to the kernel's crypto core.
156   *
157   * The more_data parameter is used to notify the crypto engine
158   * that this is an "update" operation, and that more data will
159   * be provided to calculate the final hash.
160   */
161  static
162  int qcrypto_afalg_send_to_kernel(QCryptoAFAlg *afalg,
163                                   const struct iovec *iov,
164                                   size_t niov,
165                                   bool more_data,
166                                   Error **errp)
167  {
168      int ret = 0;
169      int flags = (more_data ? MSG_MORE : 0);
170  
171      /* send data to kernel's crypto core */
172      ret = iov_send_recv_with_flags(afalg->opfd, flags, iov, niov,
173                                     0, iov_size(iov, niov), true);
174      if (ret < 0) {
175          error_setg_errno(errp, errno, "Send data to afalg-core failed");
176          ret = -1;
177      } else {
178          /* No error, so return 0 */
179          ret = 0;
180      }
181  
182      return ret;
183  }
184  
185  static
186  int qcrypto_afalg_recv_from_kernel(QCryptoAFAlg *afalg,
187                                     QCryptoHashAlgo alg,
188                                     uint8_t **result,
189                                     size_t *result_len,
190                                     Error **errp)
191  {
192      struct iovec outv;
193      int ret;
194      const int expected_len = qcrypto_hash_digest_len(alg);
195  
196      if (*result_len == 0) {
197          *result_len = expected_len;
198          *result = g_new0(uint8_t, *result_len);
199      } else if (*result_len != expected_len) {
200          error_setg(errp,
201                     "Result buffer size %zu is not match hash %d",
202                     *result_len, expected_len);
203          return -1;
204      }
205  
206      /* hash && get result */
207      outv.iov_base = *result;
208      outv.iov_len = *result_len;
209      ret = iov_send_recv(afalg->opfd, &outv, 1,
210                          0, iov_size(&outv, 1), false);
211      if (ret < 0) {
212          error_setg_errno(errp, errno, "Recv result from afalg-core failed");
213          return -1;
214      }
215  
216      return 0;
217  }
218  
219  static
220  int qcrypto_afalg_hash_update(QCryptoHash *hash,
221                                const struct iovec *iov,
222                                size_t niov,
223                                Error **errp)
224  {
225      return qcrypto_afalg_send_to_kernel((QCryptoAFAlg *) hash->opaque,
226                                          iov, niov, true, errp);
227  }
228  
229  static
230  int qcrypto_afalg_hash_finalize(QCryptoHash *hash,
231                                   uint8_t **result,
232                                   size_t *result_len,
233                                   Error **errp)
234  {
235      return qcrypto_afalg_recv_from_kernel((QCryptoAFAlg *) hash->opaque,
236                                            hash->alg, result, result_len, errp);
237  }
238  
239  static int
240  qcrypto_afalg_hash_hmac_bytesv(QCryptoAFAlgo *hmac,
241                                 QCryptoHashAlgo alg,
242                                 const struct iovec *iov,
243                                 size_t niov, uint8_t **result,
244                                 size_t *resultlen,
245                                 Error **errp)
246  {
247      QCryptoAFAlgo *afalg;
248      struct iovec outv;
249      int ret = 0;
250      bool is_hmac = (hmac != NULL) ? true : false;
251      const int expect_len = qcrypto_hash_digest_len(alg);
252  
253      if (*resultlen == 0) {
254          *resultlen = expect_len;
255          *result = g_new0(uint8_t, *resultlen);
256      } else if (*resultlen != expect_len) {
257          error_setg(errp,
258                     "Result buffer size %zu is not match hash %d",
259                     *resultlen, expect_len);
260          return -1;
261      }
262  
263      if (is_hmac) {
264          afalg = hmac;
265      } else {
266          afalg = qcrypto_afalg_hash_ctx_new(alg, errp);
267          if (!afalg) {
268              return -1;
269          }
270      }
271  
272      /* send data to kernel's crypto core */
273      ret = iov_send_recv(afalg->opfd, iov, niov,
274                          0, iov_size(iov, niov), true);
275      if (ret < 0) {
276          error_setg_errno(errp, errno, "Send data to afalg-core failed");
277          goto out;
278      }
279  
280      /* hash && get result */
281      outv.iov_base = *result;
282      outv.iov_len = *resultlen;
283      ret = iov_send_recv(afalg->opfd, &outv, 1,
284                          0, iov_size(&outv, 1), false);
285      if (ret < 0) {
286          error_setg_errno(errp, errno, "Recv result from afalg-core failed");
287      } else {
288          ret = 0;
289      }
290  
291  out:
292      if (!is_hmac) {
293          qcrypto_afalg_comm_free(afalg);
294      }
295      return ret;
296  }
297  
298  static int
299  qcrypto_afalg_hash_bytesv(QCryptoHashAlgo alg,
300                            const struct iovec *iov,
301                            size_t niov, uint8_t **result,
302                            size_t *resultlen,
303                            Error **errp)
304  {
305      return qcrypto_afalg_hash_hmac_bytesv(NULL, alg, iov, niov, result,
306                                            resultlen, errp);
307  }
308  
309  static int
310  qcrypto_afalg_hmac_bytesv(QCryptoHmac *hmac,
311                            const struct iovec *iov,
312                            size_t niov, uint8_t **result,
313                            size_t *resultlen,
314                            Error **errp)
315  {
316      return qcrypto_afalg_hash_hmac_bytesv(hmac->opaque, hmac->alg,
317                                            iov, niov, result, resultlen,
318                                            errp);
319  }
320  
321  static void qcrypto_afalg_hmac_ctx_free(QCryptoHmac *hmac)
322  {
323      QCryptoAFAlgo *afalg;
324  
325      afalg = hmac->opaque;
326      qcrypto_afalg_comm_free(afalg);
327  }
328  
329  QCryptoHashDriver qcrypto_hash_afalg_driver = {
330      .hash_bytesv = qcrypto_afalg_hash_bytesv,
331      .hash_new      = qcrypto_afalg_hash_new,
332      .hash_free     = qcrypto_afalg_hash_free,
333      .hash_update   = qcrypto_afalg_hash_update,
334      .hash_finalize = qcrypto_afalg_hash_finalize
335  };
336  
337  QCryptoHmacDriver qcrypto_hmac_afalg_driver = {
338      .hmac_bytesv = qcrypto_afalg_hmac_bytesv,
339      .hmac_free = qcrypto_afalg_hmac_ctx_free,
340  };
341