Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 1 | /* Copyright (c) 2014, Google Inc. |
| 2 | * |
| 3 | * Permission to use, copy, modify, and/or distribute this software for any |
| 4 | * purpose with or without fee is hereby granted, provided that the above |
| 5 | * copyright notice and this permission notice appear in all copies. |
| 6 | * |
| 7 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 8 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 9 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| 10 | * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 11 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION |
| 12 | * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN |
| 13 | * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ |
| 14 | |
| 15 | #include <openssl/hkdf.h> |
| 16 | |
| 17 | #include <assert.h> |
| 18 | #include <string.h> |
| 19 | |
| 20 | #include <openssl/err.h> |
| 21 | #include <openssl/hmac.h> |
| 22 | |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 23 | #include "../internal.h" |
| 24 | |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 25 | |
David Benjamin | d316cba | 2016-06-02 16:17:39 -0400 | [diff] [blame] | 26 | int HKDF(uint8_t *out_key, size_t out_len, const EVP_MD *digest, |
| 27 | const uint8_t *secret, size_t secret_len, const uint8_t *salt, |
| 28 | size_t salt_len, const uint8_t *info, size_t info_len) { |
Robert Sloan | 8f860b1 | 2017-08-28 07:37:06 -0700 | [diff] [blame] | 29 | // https://tools.ietf.org/html/rfc5869#section-2 |
David Benjamin | d316cba | 2016-06-02 16:17:39 -0400 | [diff] [blame] | 30 | uint8_t prk[EVP_MAX_MD_SIZE]; |
| 31 | size_t prk_len; |
| 32 | |
| 33 | if (!HKDF_extract(prk, &prk_len, digest, secret, secret_len, salt, |
| 34 | salt_len) || |
| 35 | !HKDF_expand(out_key, out_len, digest, prk, prk_len, info, info_len)) { |
| 36 | return 0; |
| 37 | } |
| 38 | |
| 39 | return 1; |
| 40 | } |
| 41 | |
| 42 | int HKDF_extract(uint8_t *out_key, size_t *out_len, const EVP_MD *digest, |
| 43 | const uint8_t *secret, size_t secret_len, const uint8_t *salt, |
| 44 | size_t salt_len) { |
Robert Sloan | 8f860b1 | 2017-08-28 07:37:06 -0700 | [diff] [blame] | 45 | // https://tools.ietf.org/html/rfc5869#section-2.2 |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 46 | |
Robert Sloan | 8f860b1 | 2017-08-28 07:37:06 -0700 | [diff] [blame] | 47 | // If salt is not given, HashLength zeros are used. However, HMAC does that |
| 48 | // internally already so we can ignore it. |
David Benjamin | d316cba | 2016-06-02 16:17:39 -0400 | [diff] [blame] | 49 | unsigned len; |
| 50 | if (HMAC(digest, salt, salt_len, secret, secret_len, out_key, &len) == NULL) { |
| 51 | OPENSSL_PUT_ERROR(HKDF, ERR_R_HMAC_LIB); |
| 52 | return 0; |
| 53 | } |
| 54 | *out_len = len; |
| 55 | assert(*out_len == EVP_MD_size(digest)); |
| 56 | return 1; |
| 57 | } |
| 58 | |
| 59 | int HKDF_expand(uint8_t *out_key, size_t out_len, const EVP_MD *digest, |
David Benjamin | c895d6b | 2016-08-11 13:26:41 -0400 | [diff] [blame] | 60 | const uint8_t *prk, size_t prk_len, const uint8_t *info, |
David Benjamin | d316cba | 2016-06-02 16:17:39 -0400 | [diff] [blame] | 61 | size_t info_len) { |
Robert Sloan | 8f860b1 | 2017-08-28 07:37:06 -0700 | [diff] [blame] | 62 | // https://tools.ietf.org/html/rfc5869#section-2.3 |
David Benjamin | d316cba | 2016-06-02 16:17:39 -0400 | [diff] [blame] | 63 | const size_t digest_len = EVP_MD_size(digest); |
| 64 | uint8_t previous[EVP_MAX_MD_SIZE]; |
| 65 | size_t n, done = 0; |
| 66 | unsigned i; |
| 67 | int ret = 0; |
| 68 | HMAC_CTX hmac; |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 69 | |
Robert Sloan | 8f860b1 | 2017-08-28 07:37:06 -0700 | [diff] [blame] | 70 | // Expand key material to desired length. |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 71 | n = (out_len + digest_len - 1) / digest_len; |
| 72 | if (out_len + digest_len < out_len || n > 255) { |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 73 | OPENSSL_PUT_ERROR(HKDF, HKDF_R_OUTPUT_TOO_LARGE); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 74 | return 0; |
| 75 | } |
| 76 | |
| 77 | HMAC_CTX_init(&hmac); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 78 | if (!HMAC_Init_ex(&hmac, prk, prk_len, digest, NULL)) { |
| 79 | goto out; |
| 80 | } |
| 81 | |
| 82 | for (i = 0; i < n; i++) { |
| 83 | uint8_t ctr = i + 1; |
| 84 | size_t todo; |
| 85 | |
| 86 | if (i != 0 && (!HMAC_Init_ex(&hmac, NULL, 0, NULL, NULL) || |
| 87 | !HMAC_Update(&hmac, previous, digest_len))) { |
| 88 | goto out; |
| 89 | } |
| 90 | if (!HMAC_Update(&hmac, info, info_len) || |
| 91 | !HMAC_Update(&hmac, &ctr, 1) || |
| 92 | !HMAC_Final(&hmac, previous, NULL)) { |
| 93 | goto out; |
| 94 | } |
| 95 | |
| 96 | todo = digest_len; |
| 97 | if (done + todo > out_len) { |
| 98 | todo = out_len - done; |
| 99 | } |
Robert Sloan | 69939df | 2017-01-09 10:53:07 -0800 | [diff] [blame] | 100 | OPENSSL_memcpy(out_key + done, previous, todo); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 101 | done += todo; |
| 102 | } |
| 103 | |
| 104 | ret = 1; |
| 105 | |
| 106 | out: |
| 107 | HMAC_CTX_cleanup(&hmac); |
| 108 | if (ret != 1) { |
Kenny Root | b849459 | 2015-09-25 02:29:14 +0000 | [diff] [blame] | 109 | OPENSSL_PUT_ERROR(HKDF, ERR_R_HMAC_LIB); |
Adam Langley | d9e397b | 2015-01-22 14:27:53 -0800 | [diff] [blame] | 110 | } |
| 111 | return ret; |
| 112 | } |