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