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 "qemu/osdep.h" 22 #include "crypto/hash.h" 23 24 #ifdef CONFIG_GNUTLS_HASH 25 #include <gnutls/gnutls.h> 26 #include <gnutls/crypto.h> 27 #endif 28 29 30 static size_t qcrypto_hash_alg_size[QCRYPTO_HASH_ALG__MAX] = { 31 [QCRYPTO_HASH_ALG_MD5] = 16, 32 [QCRYPTO_HASH_ALG_SHA1] = 20, 33 [QCRYPTO_HASH_ALG_SHA256] = 32, 34 }; 35 36 size_t qcrypto_hash_digest_len(QCryptoHashAlgorithm alg) 37 { 38 if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_size)) { 39 return 0; 40 } 41 return qcrypto_hash_alg_size[alg]; 42 } 43 44 45 #ifdef CONFIG_GNUTLS_HASH 46 static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = { 47 [QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5, 48 [QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1, 49 [QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256, 50 }; 51 52 gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg) 53 { 54 if (alg < G_N_ELEMENTS(qcrypto_hash_alg_map)) { 55 return true; 56 } 57 return false; 58 } 59 60 61 int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, 62 const struct iovec *iov, 63 size_t niov, 64 uint8_t **result, 65 size_t *resultlen, 66 Error **errp) 67 { 68 int i, ret; 69 gnutls_hash_hd_t dig; 70 71 if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map)) { 72 error_setg(errp, 73 "Unknown hash algorithm %d", 74 alg); 75 return -1; 76 } 77 78 ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]); 79 80 if (ret < 0) { 81 error_setg(errp, 82 "Unable to initialize hash algorithm: %s", 83 gnutls_strerror(ret)); 84 return -1; 85 } 86 87 for (i = 0; i < niov; i++) { 88 ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len); 89 if (ret < 0) { 90 error_setg(errp, 91 "Unable process hash data: %s", 92 gnutls_strerror(ret)); 93 goto error; 94 } 95 } 96 97 ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]); 98 if (ret <= 0) { 99 error_setg(errp, 100 "Unable to get hash length: %s", 101 gnutls_strerror(ret)); 102 goto error; 103 } 104 if (*resultlen == 0) { 105 *resultlen = ret; 106 *result = g_new0(uint8_t, *resultlen); 107 } else if (*resultlen != ret) { 108 error_setg(errp, 109 "Result buffer size %zu is smaller than hash %d", 110 *resultlen, ret); 111 goto error; 112 } 113 114 gnutls_hash_deinit(dig, *result); 115 return 0; 116 117 error: 118 gnutls_hash_deinit(dig, NULL); 119 return -1; 120 } 121 122 #else /* ! CONFIG_GNUTLS_HASH */ 123 124 gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg G_GNUC_UNUSED) 125 { 126 return false; 127 } 128 129 int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg, 130 const struct iovec *iov G_GNUC_UNUSED, 131 size_t niov G_GNUC_UNUSED, 132 uint8_t **result G_GNUC_UNUSED, 133 size_t *resultlen G_GNUC_UNUSED, 134 Error **errp) 135 { 136 error_setg(errp, 137 "Hash algorithm %d not supported without GNUTLS", 138 alg); 139 return -1; 140 } 141 142 #endif /* ! CONFIG_GNUTLS_HASH */ 143 144 int qcrypto_hash_bytes(QCryptoHashAlgorithm alg, 145 const char *buf, 146 size_t len, 147 uint8_t **result, 148 size_t *resultlen, 149 Error **errp) 150 { 151 struct iovec iov = { .iov_base = (char *)buf, 152 .iov_len = len }; 153 return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp); 154 } 155 156 static const char hex[] = "0123456789abcdef"; 157 158 int qcrypto_hash_digestv(QCryptoHashAlgorithm alg, 159 const struct iovec *iov, 160 size_t niov, 161 char **digest, 162 Error **errp) 163 { 164 uint8_t *result = NULL; 165 size_t resultlen = 0; 166 size_t i; 167 168 if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { 169 return -1; 170 } 171 172 *digest = g_new0(char, (resultlen * 2) + 1); 173 for (i = 0 ; i < resultlen ; i++) { 174 (*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf]; 175 (*digest)[(i * 2) + 1] = hex[result[i] & 0xf]; 176 } 177 (*digest)[resultlen * 2] = '\0'; 178 g_free(result); 179 return 0; 180 } 181 182 int qcrypto_hash_digest(QCryptoHashAlgorithm alg, 183 const char *buf, 184 size_t len, 185 char **digest, 186 Error **errp) 187 { 188 struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; 189 190 return qcrypto_hash_digestv(alg, &iov, 1, digest, errp); 191 } 192 193 int qcrypto_hash_base64v(QCryptoHashAlgorithm alg, 194 const struct iovec *iov, 195 size_t niov, 196 char **base64, 197 Error **errp) 198 { 199 uint8_t *result = NULL; 200 size_t resultlen = 0; 201 202 if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) { 203 return -1; 204 } 205 206 *base64 = g_base64_encode(result, resultlen); 207 g_free(result); 208 return 0; 209 } 210 211 int qcrypto_hash_base64(QCryptoHashAlgorithm alg, 212 const char *buf, 213 size_t len, 214 char **base64, 215 Error **errp) 216 { 217 struct iovec iov = { .iov_base = (char *)buf, .iov_len = len }; 218 219 return qcrypto_hash_base64v(alg, &iov, 1, base64, errp); 220 } 221