Alexandre Lision | 7fd5d3d | 2013-12-04 13:06:40 -0500 | [diff] [blame] | 1 | #include <stdint.h> |
| 2 | #include <stdio.h> |
| 3 | #include <stdlib.h> |
| 4 | #include <string.h> |
| 5 | |
| 6 | #include <crypto/hmac256.h> |
| 7 | |
| 8 | /* |
| 9 | HKDF-Expand(PRK, info, L) |
| 10 | |
| 11 | Description in RFC 5869 |
| 12 | |
| 13 | HKDF-Expand(PRK, info, L) -> OKM |
| 14 | |
| 15 | Options: |
| 16 | Hash a hash function; HashLen denotes the length of the |
| 17 | hash function output in octets |
| 18 | Inputs: |
| 19 | PRK a pseudorandom key of at least HashLen octets |
| 20 | (usually, the output from the extract step) |
| 21 | info optional context and application specific information |
| 22 | (can be a zero-length string) |
| 23 | L length of output keying material in octets |
| 24 | (<= 255*HashLen) |
| 25 | |
| 26 | Output: |
| 27 | OKM output keying material (of L octets) |
| 28 | |
| 29 | The output OKM is calculated as follows: |
| 30 | |
| 31 | N = ceil(L/HashLen) |
| 32 | T = T(1) | T(2) | T(3) | ... | T(N) |
| 33 | OKM = first L octets of T |
| 34 | |
| 35 | where: |
| 36 | T(0) = empty string (zero length) |
| 37 | T(1) = HMAC-Hash(PRK, T(0) | info | 0x01) |
| 38 | T(2) = HMAC-Hash(PRK, T(1) | info | 0x02) |
| 39 | T(3) = HMAC-Hash(PRK, T(2) | info | 0x03) |
| 40 | ... |
| 41 | |
| 42 | (where the constant concatenated to the end of each T(n) is a |
| 43 | single octet.) |
| 44 | |
| 45 | |
| 46 | A.1. Test Case 1 |
| 47 | |
| 48 | Basic test case with SHA-256 |
| 49 | |
| 50 | Hash = SHA-256 |
| 51 | IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (22 octets) |
| 52 | salt = 0x000102030405060708090a0b0c (13 octets) |
| 53 | info = 0xf0f1f2f3f4f5f6f7f8f9 (10 octets) |
| 54 | L = 42 |
| 55 | |
| 56 | PRK = 0x077709362c2e32df0ddc3f0dc47bba63 |
| 57 | 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) |
| 58 | OKM = 0x3cb25f25faacd57a90434f64d0362f2a |
| 59 | 2d2d0a90cf1a5a4c5db02d56ecc4c5bf |
| 60 | 34007208d5b887185865 (42 octets) |
| 61 | |
| 62 | A.2. Test Case 2 |
| 63 | |
| 64 | Test with SHA-256 and longer inputs/outputs |
| 65 | |
| 66 | Hash = SHA-256 |
| 67 | IKM = 0x000102030405060708090a0b0c0d0e0f |
| 68 | 101112131415161718191a1b1c1d1e1f |
| 69 | 202122232425262728292a2b2c2d2e2f |
| 70 | 303132333435363738393a3b3c3d3e3f |
| 71 | 404142434445464748494a4b4c4d4e4f (80 octets) |
| 72 | salt = 0x606162636465666768696a6b6c6d6e6f |
| 73 | 707172737475767778797a7b7c7d7e7f |
| 74 | 808182838485868788898a8b8c8d8e8f |
| 75 | 909192939495969798999a9b9c9d9e9f |
| 76 | a0a1a2a3a4a5a6a7a8a9aaabacadaeaf (80 octets) |
| 77 | info = 0xb0b1b2b3b4b5b6b7b8b9babbbcbdbebf |
| 78 | c0c1c2c3c4c5c6c7c8c9cacbcccdcecf |
| 79 | d0d1d2d3d4d5d6d7d8d9dadbdcdddedf |
| 80 | e0e1e2e3e4e5e6e7e8e9eaebecedeeef |
| 81 | f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff (80 octets) |
| 82 | L = 82 |
| 83 | |
| 84 | PRK = 0x06a6b88c5853361a06104c9ceb35b45c |
| 85 | ef760014904671014a193f40c15fc244 (32 octets) |
| 86 | OKM = 0xb11e398dc80327a1c8e7f78c596a4934 |
| 87 | 4f012eda2d4efad8a050cc4c19afa97c |
| 88 | 59045a99cac7827271cb41c65e590e09 |
| 89 | da3275600c2f09b8367793a9aca3db71 |
| 90 | cc30c58179ec3e87c14c01d5c1f3434f |
| 91 | 1d87 (82 octets) |
| 92 | |
| 93 | A.3. Test Case 3 |
| 94 | |
| 95 | Test with SHA-256 and zero-length salt/info |
| 96 | |
| 97 | Hash = SHA-256 |
| 98 | IKM = 0x0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b (22 octets) |
| 99 | salt = (0 octets) |
| 100 | info = (0 octets) |
| 101 | L = 42 |
| 102 | |
| 103 | PRK = 0x19ef24a32c717b167f33a91d6f648bdf |
| 104 | 96596776afdb6377ac434c1c293ccb04 (32 octets) |
| 105 | OKM = 0x8da4e775a563c18f715f802a063c5a31 |
| 106 | b8a11f5c5ee1879ec3454e5f3c738d2d |
| 107 | 9d201395faa4b61a96c8 (42 octets) |
| 108 | |
| 109 | */ |
| 110 | |
| 111 | static void hexdump(const char* title, const unsigned char *s, int l) |
| 112 | { |
| 113 | int n=0; |
| 114 | |
| 115 | if (s == NULL) return; |
| 116 | |
| 117 | fprintf(stderr, "%s",title); |
| 118 | for( ; n < l ; ++n) { |
| 119 | if((n%16) == 0) |
| 120 | fprintf(stderr, "\n%04x",n); |
| 121 | fprintf(stderr, " %02x",s[n]); |
| 122 | } |
| 123 | fprintf(stderr, "\n"); |
| 124 | } |
| 125 | |
| 126 | |
| 127 | static uint8_t info_A1[] = { |
| 128 | 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9}; |
| 129 | |
| 130 | static int32_t L_A1 = 42; |
| 131 | |
| 132 | static uint8_t PRK_A1[] = { |
| 133 | 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, |
| 134 | 0x90, 0xb6, 0xc7, 0x3b, 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, 0xb3, 0xe5}; |
| 135 | |
| 136 | static uint8_t OKM_A1[] = { |
| 137 | 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, |
| 138 | 0x2d, 0x2d, 0x0a, 0x90, 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, 0xc5, 0xbf, |
| 139 | 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, 0x58, 0x65}; // (42 octets) |
| 140 | |
| 141 | |
| 142 | static int32_t L_A3 = 42; |
| 143 | |
| 144 | static uint8_t PRK_A3[] = { |
| 145 | 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, |
| 146 | 0x96, 0x59, 0x67, 0x76, 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, 0xcb, 0x04}; // (32 octets) |
| 147 | |
| 148 | static uint8_t OKM_A3[] = { |
| 149 | 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, |
| 150 | 0xb8, 0xa1, 0x1f, 0x5c, 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, 0x8d, 0x2d, |
| 151 | 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, 0x96, 0xc8}; // (42 octets) |
| 152 | |
| 153 | |
| 154 | |
| 155 | void* createSha256HmacContext(uint8_t* key, int32_t keyLength); |
| 156 | void freeSha256HmacContext(void* ctx); |
| 157 | void hmacSha256Ctx(void* ctx, const uint8_t* data[], uint32_t dataLength[], uint8_t* mac, int32_t* macLength ); |
| 158 | |
| 159 | |
| 160 | static int expand(uint8_t* prk, uint32_t prkLen, uint8_t* info, int32_t infoLen, int32_t L, uint32_t hashLen, uint8_t* outbuffer) |
| 161 | { |
| 162 | int32_t n; |
| 163 | uint8_t *T; |
| 164 | void* hmacCtx; |
| 165 | |
| 166 | const uint8_t* data[4]; // Data pointers for HMAC data, max. 3 plus terminating NULL |
| 167 | uint32_t dataLen[4]; |
| 168 | int32_t dataIdx = 0; |
| 169 | |
| 170 | uint8_t counter; |
| 171 | int32_t macLength; |
| 172 | |
| 173 | if (prkLen < hashLen) |
| 174 | return -1; |
| 175 | |
| 176 | n = (L + (hashLen-1)) / hashLen; |
| 177 | |
| 178 | // T points to buffer that holds concatenated T(1) || T(2) || ... T(N)) |
| 179 | T = reinterpret_cast<uint8_t*>(malloc(n * hashLen)); |
| 180 | |
| 181 | // Prepare the HMAC |
| 182 | hmacCtx = createSha256HmacContext(prk, prkLen); |
| 183 | |
| 184 | // Prepare first HMAC. T(0) has zero length, thus we ignore it in first run. |
| 185 | // After first run use its output (T(1)) as first data in next HMAC run. |
| 186 | for (int i = 1; i <= n; i++) { |
| 187 | if (infoLen > 0 && info != NULL) { |
| 188 | data[dataIdx] = info; |
| 189 | dataLen[dataIdx++] = infoLen; |
| 190 | } |
| 191 | counter = i & 0xff; |
| 192 | data[dataIdx] = &counter; |
| 193 | dataLen[dataIdx++] = 1; |
| 194 | |
| 195 | data[dataIdx] = NULL; |
| 196 | dataLen[dataIdx++] = 0; |
| 197 | |
| 198 | hmacSha256Ctx(hmacCtx, data, dataLen, T + ((i-1) * hashLen), &macLength); |
| 199 | |
| 200 | dataIdx = 0; |
| 201 | data[dataIdx] = T + ((i-1) * hashLen); |
| 202 | dataLen[dataIdx++] = hashLen; |
| 203 | } |
| 204 | freeSha256HmacContext(hmacCtx); |
| 205 | memcpy(outbuffer, T, L); |
| 206 | free(T); |
| 207 | return 0; |
| 208 | } |
| 209 | |
| 210 | int main(int argc, char *argv[]) |
| 211 | { |
| 212 | uint8_t buffer[500]; |
| 213 | expand(PRK_A1, sizeof(PRK_A1), info_A1, sizeof(info_A1), L_A1, SHA256_DIGEST_LENGTH, buffer); |
| 214 | if (memcmp(buffer, OKM_A1, L_A1) != 0) { |
| 215 | fprintf(stderr, "ERROR: Test result A1 mismatch"); |
| 216 | hexdump("Computed result of expand A1", buffer, L_A1); |
| 217 | hexdump("Expected result of expand A1", OKM_A1, L_A1); |
| 218 | return 1; |
| 219 | } |
| 220 | |
| 221 | expand(PRK_A3, sizeof(PRK_A3), NULL, 0, L_A3, SHA256_DIGEST_LENGTH, buffer); |
| 222 | if (memcmp(buffer, OKM_A3, L_A3) != 0) { |
| 223 | fprintf(stderr, "ERROR: Test result A3 mismatch"); |
| 224 | hexdump("Computed result of expand A3", buffer, L_A3); |
| 225 | hexdump("Expected result of expand A3", OKM_A3, L_A3); |
| 226 | return 1; |
| 227 | } |
| 228 | |
| 229 | printf("Done\n"); |
| 230 | return 0; |
| 231 | } |