pkcs5_pbkdf2.c (3800B)
1 /* pkcs5_pbkdf2.c - Password-Based Key Derivation Function 2 2 * Copyright (c) 2008 Damien Bergamini <damien.bergamini@free.fr> 3 * 4 * Modifications for Claws Mail are: 5 * Copyright (c) 2016 the Claws Mail team 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <glib.h> 21 #include <sys/types.h> 22 23 #include <string.h> 24 #include <stdint.h> 25 #include <stdlib.h> 26 #include <stdio.h> 27 28 #define CHECKSUM_BLOCKLEN 64 29 #define SHA1_DIGESTLEN 20 30 31 /* 32 * HMAC-SHA-1 (from RFC 2202). 33 */ 34 static void 35 hmac_sha1(const guchar *text, size_t text_len, const guchar *key, 36 size_t key_len, guchar *digest) 37 { 38 GChecksum *cksum; 39 gsize outlen; 40 guchar k_pad[CHECKSUM_BLOCKLEN]; 41 guchar tk[SHA1_DIGESTLEN]; 42 gint i; 43 44 if (key_len > CHECKSUM_BLOCKLEN) { 45 cksum = g_checksum_new(G_CHECKSUM_SHA1); 46 g_checksum_update(cksum, key, key_len); 47 outlen = SHA1_DIGESTLEN; 48 g_checksum_get_digest(cksum, tk, &outlen); 49 g_checksum_free(cksum); 50 51 key = tk; 52 key_len = SHA1_DIGESTLEN; 53 } 54 55 memset(k_pad, 0, sizeof k_pad); 56 memcpy(k_pad, key, key_len); 57 for (i = 0; i < CHECKSUM_BLOCKLEN; i++) 58 k_pad[i] ^= 0x36; 59 60 cksum = g_checksum_new(G_CHECKSUM_SHA1); 61 g_checksum_update(cksum, k_pad, CHECKSUM_BLOCKLEN); 62 g_checksum_update(cksum, text, text_len); 63 outlen = SHA1_DIGESTLEN; 64 g_checksum_get_digest(cksum, digest, &outlen); 65 g_checksum_free(cksum); 66 67 memset(k_pad, 0, sizeof k_pad); 68 memcpy(k_pad, key, key_len); 69 for (i = 0; i < CHECKSUM_BLOCKLEN; i++) 70 k_pad[i] ^= 0x5c; 71 72 cksum = g_checksum_new(G_CHECKSUM_SHA1); 73 g_checksum_update(cksum, k_pad, CHECKSUM_BLOCKLEN); 74 g_checksum_update(cksum, digest, SHA1_DIGESTLEN); 75 outlen = SHA1_DIGESTLEN; 76 g_checksum_get_digest(cksum, digest, &outlen); 77 g_checksum_free(cksum); 78 } 79 80 #undef CHECKSUM_BLOCKLEN 81 82 /* 83 * Password-Based Key Derivation Function 2 (PKCS #5 v2.0). 84 * Code based on IEEE Std 802.11-2007, Annex H.4.2. 85 */ 86 gint 87 pkcs5_pbkdf2(const gchar *pass, size_t pass_len, const guchar *salt, 88 size_t salt_len, guchar *key, size_t key_len, guint rounds) 89 { 90 guchar *asalt, obuf[SHA1_DIGESTLEN]; 91 guchar d1[SHA1_DIGESTLEN], d2[SHA1_DIGESTLEN]; 92 guint i, j; 93 guint count; 94 size_t r; 95 96 if (pass == NULL || salt == NULL || key == NULL) 97 return -1; 98 if (rounds < 1 || key_len == 0) 99 return -1; 100 if (salt_len == 0 || salt_len > SIZE_MAX - 4) 101 return -1; 102 if ((asalt = malloc(salt_len + 4)) == NULL) 103 return -1; 104 105 memcpy(asalt, salt, salt_len); 106 107 for (count = 1; key_len > 0; count++) { 108 asalt[salt_len + 0] = (count >> 24) & 0xff; 109 asalt[salt_len + 1] = (count >> 16) & 0xff; 110 asalt[salt_len + 2] = (count >> 8) & 0xff; 111 asalt[salt_len + 3] = count & 0xff; 112 hmac_sha1(asalt, salt_len + 4, pass, pass_len, d1); 113 memcpy(obuf, d1, sizeof(obuf)); 114 115 for (i = 1; i < rounds; i++) { 116 hmac_sha1(d1, sizeof(d1), pass, pass_len, d2); 117 memcpy(d1, d2, sizeof(d1)); 118 for (j = 0; j < sizeof(obuf); j++) 119 obuf[j] ^= d1[j]; 120 } 121 122 r = MIN(key_len, SHA1_DIGESTLEN); 123 memcpy(key, obuf, r); 124 key += r; 125 key_len -= r; 126 }; 127 memset(asalt, 0, salt_len + 4); 128 free(asalt); 129 memset(d1, 0, sizeof(d1)); 130 memset(d2, 0, sizeof(d2)); 131 memset(obuf, 0, sizeof(obuf)); 132 133 return 0; 134 } 135 136 #undef SHA1_DIGESTLEN