xref: /openbmc/qemu/crypto/hash.c (revision b45c03f5)
1 /*
2  * QEMU Crypto hash algorithms
3  *
4  * Copyright (c) 2015 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "crypto/hash.h"
22 
23 #ifdef CONFIG_GNUTLS_HASH
24 #include <gnutls/gnutls.h>
25 #include <gnutls/crypto.h>
26 
27 static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = {
28     [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
29     [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
30     [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
31 };
32 
33 gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
34 {
35     if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) {
36         return true;
37     }
38     return false;
39 }
40 
41 int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
42                         const struct iovec *iov,
43                         size_t niov,
44                         uint8_t **result,
45                         size_t *resultlen,
46                         Error **errp)
47 {
48     int i, ret;
49     gnutls_hash_hd_t dig;
50 
51     if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map)) {
52         error_setg(errp,
53                    "Unknown hash algorithm %d",
54                    alg);
55         return -1;
56     }
57 
58     ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]);
59 
60     if (ret < 0) {
61         error_setg(errp,
62                    "Unable to initialize hash algorithm: %s",
63                    gnutls_strerror(ret));
64         return -1;
65     }
66 
67     for (i = 0; i < niov; i++) {
68         ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len);
69         if (ret < 0) {
70             error_setg(errp,
71                        "Unable process hash data: %s",
72                        gnutls_strerror(ret));
73             goto error;
74         }
75     }
76 
77     ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]);
78     if (ret <= 0) {
79         error_setg(errp,
80                    "Unable to get hash length: %s",
81                    gnutls_strerror(ret));
82         goto error;
83     }
84     if (*resultlen == 0) {
85         *resultlen = ret;
86         *result = g_new0(uint8_t, *resultlen);
87     } else if (*resultlen != ret) {
88         error_setg(errp,
89                    "Result buffer size %zu is smaller than hash %d",
90                    *resultlen, ret);
91         goto error;
92     }
93 
94     gnutls_hash_deinit(dig, *result);
95     return 0;
96 
97  error:
98     gnutls_hash_deinit(dig, NULL);
99     return -1;
100 }
101 
102 #else /* ! CONFIG_GNUTLS_HASH */
103 
104 gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED)
105 {
106     return false;
107 }
108 
109 int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
110                         const struct iovec *iov G_GNUC_UNUSED,
111                         size_t niov G_GNUC_UNUSED,
112                         uint8_t **result G_GNUC_UNUSED,
113                         size_t *resultlen G_GNUC_UNUSED,
114                         Error **errp)
115 {
116     error_setg(errp,
117                "Hash algorithm %d not supported without GNUTLS",
118                alg);
119     return -1;
120 }
121 
122 #endif /* ! CONFIG_GNUTLS_HASH */
123 
124 int qcrypto_hash_bytes(QCryptoHashAlgorithm alg,
125                        const char *buf,
126                        size_t len,
127                        uint8_t **result,
128                        size_t *resultlen,
129                        Error **errp)
130 {
131     struct iovec iov = { .iov_base = (char *)buf,
132                          .iov_len = len };
133     return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp);
134 }
135 
136 static const char hex[] = "0123456789abcdef";
137 
138 int qcrypto_hash_digestv(QCryptoHashAlgorithm alg,
139                          const struct iovec *iov,
140                          size_t niov,
141                          char **digest,
142                          Error **errp)
143 {
144     uint8_t *result = NULL;
145     size_t resultlen = 0;
146     size_t i;
147 
148     if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
149         return -1;
150     }
151 
152     *digest = g_new0(char, (resultlen * 2) + 1);
153     for (i = 0 ; i < resultlen ; i++) {
154         (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf];
155         (*digest)[(i * 2) + 1] = hex[result[i] & 0xf];
156     }
157     (*digest)[resultlen * 2] = '\0';
158     g_free(result);
159     return 0;
160 }
161 
162 int qcrypto_hash_digest(QCryptoHashAlgorithm alg,
163                         const char *buf,
164                         size_t len,
165                         char **digest,
166                         Error **errp)
167 {
168     struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
169 
170     return qcrypto_hash_digestv(alg, &iov, 1, digest, errp);
171 }
172 
173 int qcrypto_hash_base64v(QCryptoHashAlgorithm alg,
174                          const struct iovec *iov,
175                          size_t niov,
176                          char **base64,
177                          Error **errp)
178 {
179     uint8_t *result = NULL;
180     size_t resultlen = 0;
181 
182     if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
183         return -1;
184     }
185 
186     *base64 = g_base64_encode(result, resultlen);
187     g_free(result);
188     return 0;
189 }
190 
191 int qcrypto_hash_base64(QCryptoHashAlgorithm alg,
192                         const char *buf,
193                         size_t len,
194                         char **base64,
195                         Error **errp)
196 {
197     struct iovec iov = { .iov_base = (char *)buf, .iov_len = len };
198 
199     return qcrypto_hash_base64v(alg, &iov, 1, base64, errp);
200 }
201