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