| 1 | /* |
| 2 | * Copyright (C) 2012 Martin Storsjo |
| 3 | * |
| 4 | * This file is part of FFmpeg. |
| 5 | * |
| 6 | * FFmpeg 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.1 of the License, or (at your option) any later version. |
| 10 | * |
| 11 | * FFmpeg 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 FFmpeg; if not, write to the Free Software |
| 18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
| 19 | */ |
| 20 | |
| 21 | #include <string.h> |
| 22 | |
| 23 | #include "attributes.h" |
| 24 | #include "hmac.h" |
| 25 | #include "md5.h" |
| 26 | #include "sha.h" |
| 27 | #include "sha512.h" |
| 28 | #include "mem.h" |
| 29 | |
| 30 | #define MAX_HASHLEN 64 |
| 31 | #define MAX_BLOCKLEN 128 |
| 32 | |
| 33 | struct AVHMAC { |
| 34 | void *hash; |
| 35 | int blocklen, hashlen; |
| 36 | void (*final)(void*, uint8_t*); |
| 37 | void (*update)(void*, const uint8_t*, int len); |
| 38 | void (*init)(void*); |
| 39 | uint8_t key[MAX_BLOCKLEN]; |
| 40 | int keylen; |
| 41 | }; |
| 42 | |
| 43 | #define DEFINE_SHA(bits) \ |
| 44 | static av_cold void sha ## bits ##_init(void *ctx) \ |
| 45 | { \ |
| 46 | av_sha_init(ctx, bits); \ |
| 47 | } |
| 48 | |
| 49 | #define DEFINE_SHA512(bits) \ |
| 50 | static av_cold void sha ## bits ##_init(void *ctx) \ |
| 51 | { \ |
| 52 | av_sha512_init(ctx, bits); \ |
| 53 | } |
| 54 | |
| 55 | DEFINE_SHA(160) |
| 56 | DEFINE_SHA(224) |
| 57 | DEFINE_SHA(256) |
| 58 | DEFINE_SHA512(384) |
| 59 | DEFINE_SHA512(512) |
| 60 | |
| 61 | AVHMAC *av_hmac_alloc(enum AVHMACType type) |
| 62 | { |
| 63 | AVHMAC *c = av_mallocz(sizeof(*c)); |
| 64 | if (!c) |
| 65 | return NULL; |
| 66 | switch (type) { |
| 67 | case AV_HMAC_MD5: |
| 68 | c->blocklen = 64; |
| 69 | c->hashlen = 16; |
| 70 | c->init = (void*)av_md5_init; |
| 71 | c->update = (void*)av_md5_update; |
| 72 | c->final = (void*)av_md5_final; |
| 73 | c->hash = av_md5_alloc(); |
| 74 | break; |
| 75 | case AV_HMAC_SHA1: |
| 76 | c->blocklen = 64; |
| 77 | c->hashlen = 20; |
| 78 | c->init = sha160_init; |
| 79 | c->update = (void*)av_sha_update; |
| 80 | c->final = (void*)av_sha_final; |
| 81 | c->hash = av_sha_alloc(); |
| 82 | break; |
| 83 | case AV_HMAC_SHA224: |
| 84 | c->blocklen = 64; |
| 85 | c->hashlen = 28; |
| 86 | c->init = sha224_init; |
| 87 | c->update = (void*)av_sha_update; |
| 88 | c->final = (void*)av_sha_final; |
| 89 | c->hash = av_sha_alloc(); |
| 90 | break; |
| 91 | case AV_HMAC_SHA256: |
| 92 | c->blocklen = 64; |
| 93 | c->hashlen = 32; |
| 94 | c->init = sha256_init; |
| 95 | c->update = (void*)av_sha_update; |
| 96 | c->final = (void*)av_sha_final; |
| 97 | c->hash = av_sha_alloc(); |
| 98 | break; |
| 99 | case AV_HMAC_SHA384: |
| 100 | c->blocklen = 128; |
| 101 | c->hashlen = 48; |
| 102 | c->init = sha384_init; |
| 103 | c->update = (void*)av_sha512_update; |
| 104 | c->final = (void*)av_sha512_final; |
| 105 | c->hash = av_sha512_alloc(); |
| 106 | break; |
| 107 | case AV_HMAC_SHA512: |
| 108 | c->blocklen = 128; |
| 109 | c->hashlen = 64; |
| 110 | c->init = sha512_init; |
| 111 | c->update = (void*)av_sha512_update; |
| 112 | c->final = (void*)av_sha512_final; |
| 113 | c->hash = av_sha512_alloc(); |
| 114 | break; |
| 115 | default: |
| 116 | av_free(c); |
| 117 | return NULL; |
| 118 | } |
| 119 | if (!c->hash) { |
| 120 | av_free(c); |
| 121 | return NULL; |
| 122 | } |
| 123 | return c; |
| 124 | } |
| 125 | |
| 126 | void av_hmac_free(AVHMAC *c) |
| 127 | { |
| 128 | if (!c) |
| 129 | return; |
| 130 | av_free(c->hash); |
| 131 | av_free(c); |
| 132 | } |
| 133 | |
| 134 | void av_hmac_init(AVHMAC *c, const uint8_t *key, unsigned int keylen) |
| 135 | { |
| 136 | int i; |
| 137 | uint8_t block[MAX_BLOCKLEN]; |
| 138 | if (keylen > c->blocklen) { |
| 139 | c->init(c->hash); |
| 140 | c->update(c->hash, key, keylen); |
| 141 | c->final(c->hash, c->key); |
| 142 | c->keylen = c->hashlen; |
| 143 | } else { |
| 144 | memcpy(c->key, key, keylen); |
| 145 | c->keylen = keylen; |
| 146 | } |
| 147 | c->init(c->hash); |
| 148 | for (i = 0; i < c->keylen; i++) |
| 149 | block[i] = c->key[i] ^ 0x36; |
| 150 | for (i = c->keylen; i < c->blocklen; i++) |
| 151 | block[i] = 0x36; |
| 152 | c->update(c->hash, block, c->blocklen); |
| 153 | } |
| 154 | |
| 155 | void av_hmac_update(AVHMAC *c, const uint8_t *data, unsigned int len) |
| 156 | { |
| 157 | c->update(c->hash, data, len); |
| 158 | } |
| 159 | |
| 160 | int av_hmac_final(AVHMAC *c, uint8_t *out, unsigned int outlen) |
| 161 | { |
| 162 | uint8_t block[MAX_BLOCKLEN]; |
| 163 | int i; |
| 164 | if (outlen < c->hashlen) |
| 165 | return AVERROR(EINVAL); |
| 166 | c->final(c->hash, out); |
| 167 | c->init(c->hash); |
| 168 | for (i = 0; i < c->keylen; i++) |
| 169 | block[i] = c->key[i] ^ 0x5C; |
| 170 | for (i = c->keylen; i < c->blocklen; i++) |
| 171 | block[i] = 0x5C; |
| 172 | c->update(c->hash, block, c->blocklen); |
| 173 | c->update(c->hash, out, c->hashlen); |
| 174 | c->final(c->hash, out); |
| 175 | return c->hashlen; |
| 176 | } |
| 177 | |
| 178 | int av_hmac_calc(AVHMAC *c, const uint8_t *data, unsigned int len, |
| 179 | const uint8_t *key, unsigned int keylen, |
| 180 | uint8_t *out, unsigned int outlen) |
| 181 | { |
| 182 | av_hmac_init(c, key, keylen); |
| 183 | av_hmac_update(c, data, len); |
| 184 | return av_hmac_final(c, out, outlen); |
| 185 | } |
| 186 | |
| 187 | #ifdef TEST |
| 188 | #include <stdio.h> |
| 189 | |
| 190 | static void test(AVHMAC *hmac, const uint8_t *key, int keylen, |
| 191 | const uint8_t *data, int datalen) |
| 192 | { |
| 193 | uint8_t buf[MAX_HASHLEN]; |
| 194 | int out, i; |
| 195 | // Some of the test vectors are strings, where sizeof() includes the |
| 196 | // trailing null byte - remove that. |
| 197 | if (!key[keylen - 1]) |
| 198 | keylen--; |
| 199 | if (!data[datalen - 1]) |
| 200 | datalen--; |
| 201 | out = av_hmac_calc(hmac, data, datalen, key, keylen, buf, sizeof(buf)); |
| 202 | for (i = 0; i < out; i++) |
| 203 | printf("%02x", buf[i]); |
| 204 | printf("\n"); |
| 205 | } |
| 206 | |
| 207 | int main(void) |
| 208 | { |
| 209 | uint8_t key1[20], key3[131], data3[50]; |
| 210 | enum AVHMACType i = AV_HMAC_SHA224; |
| 211 | static const uint8_t key2[] = "Jefe"; |
| 212 | static const uint8_t data1[] = "Hi There"; |
| 213 | static const uint8_t data2[] = "what do ya want for nothing?"; |
| 214 | static const uint8_t data4[] = "Test Using Larger Than Block-Size Key - Hash Key First"; |
| 215 | static const uint8_t data5[] = "Test Using Larger Than Block-Size Key and Larger Than One Block-Size Data"; |
| 216 | static const uint8_t data6[] = "This is a test using a larger than block-size key and a larger " |
| 217 | "than block-size data. The key needs to be hashed before being used" |
| 218 | " by the HMAC algorithm."; |
| 219 | AVHMAC *hmac = av_hmac_alloc(AV_HMAC_MD5); |
| 220 | if (!hmac) |
| 221 | return 1; |
| 222 | memset(key1, 0x0b, sizeof(key1)); |
| 223 | memset(key3, 0xaa, sizeof(key3)); |
| 224 | memset(data3, 0xdd, sizeof(data3)); |
| 225 | // RFC 2202 test vectors |
| 226 | test(hmac, key1, 16, data1, sizeof(data1)); |
| 227 | test(hmac, key2, sizeof(key2), data2, sizeof(data2)); |
| 228 | test(hmac, key3, 16, data3, sizeof(data3)); |
| 229 | test(hmac, key3, 80, data4, sizeof(data4)); |
| 230 | test(hmac, key3, 80, data5, sizeof(data5)); |
| 231 | av_hmac_free(hmac); |
| 232 | |
| 233 | /* SHA-1 */ |
| 234 | hmac = av_hmac_alloc(AV_HMAC_SHA1); |
| 235 | if (!hmac) |
| 236 | return 1; |
| 237 | // RFC 2202 test vectors |
| 238 | test(hmac, key1, sizeof(key1), data1, sizeof(data1)); |
| 239 | test(hmac, key2, sizeof(key2), data2, sizeof(data2)); |
| 240 | test(hmac, key3, 20, data3, sizeof(data3)); |
| 241 | test(hmac, key3, 80, data4, sizeof(data4)); |
| 242 | test(hmac, key3, 80, data5, sizeof(data5)); |
| 243 | av_hmac_free(hmac); |
| 244 | |
| 245 | /* SHA-2 */ |
| 246 | while (i <= AV_HMAC_SHA512) { |
| 247 | hmac = av_hmac_alloc(i); |
| 248 | // RFC 4231 test vectors |
| 249 | test(hmac, key1, sizeof(key1), data1, sizeof(data1)); |
| 250 | test(hmac, key2, sizeof(key2), data2, sizeof(data2)); |
| 251 | test(hmac, key3, 20, data3, sizeof(data3)); |
| 252 | test(hmac, key3, sizeof(key3), data4, sizeof(data4)); |
| 253 | test(hmac, key3, sizeof(key3), data6, sizeof(data6)); |
| 254 | av_hmac_free(hmac); |
| 255 | i++; |
| 256 | } |
| 257 | return 0; |
| 258 | } |
| 259 | #endif /* TEST */ |