| 1 | /* |
| 2 | * QEMU Crypto hash algorithms |
| 3 | * |
| 4 | * Copyright (c) 2024 Seagate Technology LLC and/or its Affiliates |
| 5 | * Copyright (c) 2015 Red Hat, Inc. |
| 6 | * |
| 7 | * This library is free software; you can redistribute it and/or |
| 8 | * modify it under the terms of the GNU Lesser General Public |
| 9 | * License as published by the Free Software Foundation; either |
| 10 | * version 2.1 of the License, or (at your option) any later version. |
| 11 | * |
| 12 | * This library is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 | * Lesser General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU Lesser General Public |
| 18 | * License along with this library; if not, see <http://www.gnu.org/licenses/>. |
| 19 | * |
| 20 | */ |
| 21 | |
| 22 | #include "qemu/osdep.h" |
| 23 | |
| 24 | #include "crypto/init.h" |
| 25 | #include "crypto/hash.h" |
| 26 | |
| 27 | #define INPUT_TEXT "Hiss hisss Hissss hiss Hiss hisss Hiss hiss" |
| 28 | #define INPUT_TEXT1 "Hiss hisss " |
| 29 | #define INPUT_TEXT2 "Hissss hiss " |
| 30 | #define INPUT_TEXT3 "Hiss hisss Hiss hiss" |
| 31 | |
| 32 | #define OUTPUT_MD5 "628d206371563035ab8ef62f492bdec9" |
| 33 | #define OUTPUT_SHA1 "b2e74f26758a3a421e509cee045244b78753cc02" |
| 34 | #define OUTPUT_SHA224 "e2f7415aad33ef79f6516b0986d7175f" \ |
| 35 | "9ca3389a85bf6cfed078737b" |
| 36 | #define OUTPUT_SHA256 "bc757abb0436586f392b437e5dd24096" \ |
| 37 | "f7f224de6b74d4d86e2abc6121b160d0" |
| 38 | #define OUTPUT_SHA384 "887ce52efb4f46700376356583b7e279" \ |
| 39 | "4f612bd024e4495087ddb946c448c69d" \ |
| 40 | "56dbf7152a94a5e63a80f3ba9f0eed78" |
| 41 | #define OUTPUT_SHA512 "3a90d79638235ec6c4c11bebd84d83c0" \ |
| 42 | "549bc1e84edc4b6ec7086487641256cb" \ |
| 43 | "63b54e4cb2d2032b393994aa263c0dbb" \ |
| 44 | "e00a9f2fe9ef6037352232a1eec55ee7" |
| 45 | #define OUTPUT_RIPEMD160 "f3d658fad3fdfb2b52c9369cf0d441249ddfa8a0" |
| 46 | #ifdef CONFIG_CRYPTO_SM3 |
| 47 | #define OUTPUT_SM3 "d4a97db105b477b84c4f20ec9c31a6c814e2705a0b83a5a89748d75f0ef456a1" |
| 48 | #endif |
| 49 | |
| 50 | #define OUTPUT_MD5_B64 "Yo0gY3FWMDWrjvYvSSveyQ==" |
| 51 | #define OUTPUT_SHA1_B64 "sudPJnWKOkIeUJzuBFJEt4dTzAI=" |
| 52 | #define OUTPUT_SHA224_B64 "4vdBWq0z73n2UWsJhtcXX5yjOJqFv2z+0Hhzew==" |
| 53 | #define OUTPUT_SHA256_B64 "vHV6uwQ2WG85K0N+XdJAlvfyJN5rdNTYbiq8YSGxYNA=" |
| 54 | #define OUTPUT_SHA384_B64 "iHzlLvtPRnADdjVlg7fieU9hK9Ak5ElQh925RsRI" \ |
| 55 | "xp1W2/cVKpSl5jqA87qfDu14" |
| 56 | #define OUTPUT_SHA512_B64 "OpDXljgjXsbEwRvr2E2DwFSbwehO3Etuxwhkh2QS" \ |
| 57 | "VstjtU5MstIDKzk5lKomPA274AqfL+nvYDc1IjKh" \ |
| 58 | "7sVe5w==" |
| 59 | #define OUTPUT_RIPEMD160_B64 "89ZY+tP9+ytSyTac8NRBJJ3fqKA=" |
| 60 | |
| 61 | #ifdef CONFIG_CRYPTO_SM3 |
| 62 | #define OUTPUT_SM3_B64 "1Kl9sQW0d7hMTyDsnDGmyBTicFoLg6Wol0jXXw70VqE=" |
| 63 | #endif |
| 64 | |
| 65 | static const char *expected_outputs[] = { |
| 66 | [QCRYPTO_HASH_ALGO_MD5] = OUTPUT_MD5, |
| 67 | [QCRYPTO_HASH_ALGO_SHA1] = OUTPUT_SHA1, |
| 68 | [QCRYPTO_HASH_ALGO_SHA224] = OUTPUT_SHA224, |
| 69 | [QCRYPTO_HASH_ALGO_SHA256] = OUTPUT_SHA256, |
| 70 | [QCRYPTO_HASH_ALGO_SHA384] = OUTPUT_SHA384, |
| 71 | [QCRYPTO_HASH_ALGO_SHA512] = OUTPUT_SHA512, |
| 72 | [QCRYPTO_HASH_ALGO_RIPEMD160] = OUTPUT_RIPEMD160, |
| 73 | #ifdef CONFIG_CRYPTO_SM3 |
| 74 | [QCRYPTO_HASH_ALGO_SM3] = OUTPUT_SM3, |
| 75 | #endif |
| 76 | }; |
| 77 | static const char *expected_outputs_b64[] = { |
| 78 | [QCRYPTO_HASH_ALGO_MD5] = OUTPUT_MD5_B64, |
| 79 | [QCRYPTO_HASH_ALGO_SHA1] = OUTPUT_SHA1_B64, |
| 80 | [QCRYPTO_HASH_ALGO_SHA224] = OUTPUT_SHA224_B64, |
| 81 | [QCRYPTO_HASH_ALGO_SHA256] = OUTPUT_SHA256_B64, |
| 82 | [QCRYPTO_HASH_ALGO_SHA384] = OUTPUT_SHA384_B64, |
| 83 | [QCRYPTO_HASH_ALGO_SHA512] = OUTPUT_SHA512_B64, |
| 84 | [QCRYPTO_HASH_ALGO_RIPEMD160] = OUTPUT_RIPEMD160_B64, |
| 85 | #ifdef CONFIG_CRYPTO_SM3 |
| 86 | [QCRYPTO_HASH_ALGO_SM3] = OUTPUT_SM3_B64, |
| 87 | #endif |
| 88 | }; |
| 89 | static const int expected_lens[] = { |
| 90 | [QCRYPTO_HASH_ALGO_MD5] = 16, |
| 91 | [QCRYPTO_HASH_ALGO_SHA1] = 20, |
| 92 | [QCRYPTO_HASH_ALGO_SHA224] = 28, |
| 93 | [QCRYPTO_HASH_ALGO_SHA256] = 32, |
| 94 | [QCRYPTO_HASH_ALGO_SHA384] = 48, |
| 95 | [QCRYPTO_HASH_ALGO_SHA512] = 64, |
| 96 | [QCRYPTO_HASH_ALGO_RIPEMD160] = 20, |
| 97 | #ifdef CONFIG_CRYPTO_SM3 |
| 98 | [QCRYPTO_HASH_ALGO_SM3] = 32, |
| 99 | #endif |
| 100 | }; |
| 101 | |
| 102 | static const char hex[] = "0123456789abcdef"; |
| 103 | |
| 104 | /* Test with dynamic allocation */ |
| 105 | static void test_hash_alloc(void) |
| 106 | { |
| 107 | size_t i; |
| 108 | |
| 109 | for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { |
| 110 | uint8_t *result = NULL; |
| 111 | size_t resultlen = 0; |
| 112 | int ret; |
| 113 | size_t j; |
| 114 | |
| 115 | if (!qcrypto_hash_supports(i)) { |
| 116 | continue; |
| 117 | } |
| 118 | |
| 119 | ret = qcrypto_hash_bytes(i, |
| 120 | INPUT_TEXT, |
| 121 | strlen(INPUT_TEXT), |
| 122 | &result, |
| 123 | &resultlen, |
| 124 | &error_fatal); |
| 125 | g_assert(ret == 0); |
| 126 | g_assert(resultlen == expected_lens[i]); |
| 127 | |
| 128 | for (j = 0; j < resultlen; j++) { |
| 129 | g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); |
| 130 | g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); |
| 131 | } |
| 132 | g_free(result); |
| 133 | } |
| 134 | } |
| 135 | |
| 136 | /* Test with caller preallocating */ |
| 137 | static void test_hash_prealloc(void) |
| 138 | { |
| 139 | size_t i; |
| 140 | |
| 141 | for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { |
| 142 | uint8_t *result, *origresult; |
| 143 | size_t resultlen; |
| 144 | int ret; |
| 145 | size_t j; |
| 146 | |
| 147 | if (!qcrypto_hash_supports(i)) { |
| 148 | continue; |
| 149 | } |
| 150 | |
| 151 | resultlen = expected_lens[i]; |
| 152 | origresult = result = g_new0(uint8_t, resultlen); |
| 153 | |
| 154 | ret = qcrypto_hash_bytes(i, |
| 155 | INPUT_TEXT, |
| 156 | strlen(INPUT_TEXT), |
| 157 | &result, |
| 158 | &resultlen, |
| 159 | &error_fatal); |
| 160 | g_assert(ret == 0); |
| 161 | /* Validate that our pre-allocated pointer was not replaced */ |
| 162 | g_assert(result == origresult); |
| 163 | g_assert(resultlen == expected_lens[i]); |
| 164 | for (j = 0; j < resultlen; j++) { |
| 165 | g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); |
| 166 | g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); |
| 167 | } |
| 168 | g_free(result); |
| 169 | } |
| 170 | } |
| 171 | |
| 172 | |
| 173 | /* Test with dynamic allocation */ |
| 174 | static void test_hash_iov(void) |
| 175 | { |
| 176 | size_t i; |
| 177 | |
| 178 | for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { |
| 179 | struct iovec iov[3] = { |
| 180 | { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, |
| 181 | { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, |
| 182 | { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, |
| 183 | }; |
| 184 | uint8_t *result = NULL; |
| 185 | size_t resultlen = 0; |
| 186 | int ret; |
| 187 | size_t j; |
| 188 | |
| 189 | if (!qcrypto_hash_supports(i)) { |
| 190 | continue; |
| 191 | } |
| 192 | |
| 193 | ret = qcrypto_hash_bytesv(i, |
| 194 | iov, 3, |
| 195 | &result, |
| 196 | &resultlen, |
| 197 | &error_fatal); |
| 198 | g_assert(ret == 0); |
| 199 | g_assert(resultlen == expected_lens[i]); |
| 200 | for (j = 0; j < resultlen; j++) { |
| 201 | g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); |
| 202 | g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); |
| 203 | } |
| 204 | g_free(result); |
| 205 | } |
| 206 | } |
| 207 | |
| 208 | |
| 209 | /* Test with printable hashing */ |
| 210 | static void test_hash_digest(void) |
| 211 | { |
| 212 | size_t i; |
| 213 | |
| 214 | for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { |
| 215 | int ret; |
| 216 | char *digest; |
| 217 | size_t digestsize; |
| 218 | |
| 219 | if (!qcrypto_hash_supports(i)) { |
| 220 | continue; |
| 221 | } |
| 222 | |
| 223 | digestsize = qcrypto_hash_digest_len(i); |
| 224 | |
| 225 | g_assert_cmpint(digestsize * 2, ==, strlen(expected_outputs[i])); |
| 226 | |
| 227 | ret = qcrypto_hash_digest(i, |
| 228 | INPUT_TEXT, |
| 229 | strlen(INPUT_TEXT), |
| 230 | &digest, |
| 231 | &error_fatal); |
| 232 | g_assert(ret == 0); |
| 233 | g_assert_cmpstr(digest, ==, expected_outputs[i]); |
| 234 | g_free(digest); |
| 235 | } |
| 236 | } |
| 237 | |
| 238 | /* Test with base64 encoding */ |
| 239 | static void test_hash_base64(void) |
| 240 | { |
| 241 | size_t i; |
| 242 | |
| 243 | for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { |
| 244 | int ret; |
| 245 | char *digest; |
| 246 | |
| 247 | if (!qcrypto_hash_supports(i)) { |
| 248 | continue; |
| 249 | } |
| 250 | |
| 251 | ret = qcrypto_hash_base64(i, |
| 252 | INPUT_TEXT, |
| 253 | strlen(INPUT_TEXT), |
| 254 | &digest, |
| 255 | &error_fatal); |
| 256 | g_assert(ret == 0); |
| 257 | g_assert_cmpstr(digest, ==, expected_outputs_b64[i]); |
| 258 | g_free(digest); |
| 259 | } |
| 260 | } |
| 261 | |
| 262 | static void test_hash_accumulate(void) |
| 263 | { |
| 264 | size_t i; |
| 265 | |
| 266 | for (i = 0; i < G_N_ELEMENTS(expected_outputs) ; i++) { |
| 267 | g_autoptr(QCryptoHash) hash = NULL; |
| 268 | struct iovec iov[] = { |
| 269 | { .iov_base = (char *)INPUT_TEXT1, .iov_len = strlen(INPUT_TEXT1) }, |
| 270 | { .iov_base = (char *)INPUT_TEXT2, .iov_len = strlen(INPUT_TEXT2) }, |
| 271 | { .iov_base = (char *)INPUT_TEXT3, .iov_len = strlen(INPUT_TEXT3) }, |
| 272 | }; |
| 273 | g_autofree uint8_t *result = NULL; |
| 274 | size_t resultlen = 0; |
| 275 | int ret; |
| 276 | size_t j; |
| 277 | |
| 278 | if (!qcrypto_hash_supports(i)) { |
| 279 | continue; |
| 280 | } |
| 281 | |
| 282 | hash = qcrypto_hash_new(i, &error_fatal); |
| 283 | g_assert(hash != NULL); |
| 284 | |
| 285 | /* Add each iovec to the hash context separately */ |
| 286 | for (j = 0; j < G_N_ELEMENTS(iov); j++) { |
| 287 | ret = qcrypto_hash_updatev(hash, |
| 288 | &iov[j], 1, |
| 289 | &error_fatal); |
| 290 | |
| 291 | g_assert(ret == 0); |
| 292 | } |
| 293 | |
| 294 | ret = qcrypto_hash_finalize_bytes(hash, &result, &resultlen, |
| 295 | &error_fatal); |
| 296 | |
| 297 | g_assert(ret == 0); |
| 298 | g_assert(resultlen == expected_lens[i]); |
| 299 | for (j = 0; j < resultlen; j++) { |
| 300 | g_assert(expected_outputs[i][j * 2] == hex[(result[j] >> 4) & 0xf]); |
| 301 | g_assert(expected_outputs[i][j * 2 + 1] == hex[result[j] & 0xf]); |
| 302 | } |
| 303 | } |
| 304 | } |
| 305 | |
| 306 | int main(int argc, char **argv) |
| 307 | { |
| 308 | int ret = qcrypto_init(&error_fatal); |
| 309 | g_assert(ret == 0); |
| 310 | |
| 311 | g_test_init(&argc, &argv, NULL); |
| 312 | g_test_add_func("/crypto/hash/iov", test_hash_iov); |
| 313 | g_test_add_func("/crypto/hash/alloc", test_hash_alloc); |
| 314 | g_test_add_func("/crypto/hash/prealloc", test_hash_prealloc); |
| 315 | g_test_add_func("/crypto/hash/digest", test_hash_digest); |
| 316 | g_test_add_func("/crypto/hash/base64", test_hash_base64); |
| 317 | g_test_add_func("/crypto/hash/accumulate", test_hash_accumulate); |
| 318 | return g_test_run(); |
| 319 | } |