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