diff options
-rw-r--r-- | src/lib/kdf/hkdf/hkdf.cpp | 48 | ||||
-rw-r--r-- | src/lib/kdf/hkdf/hkdf.h | 14 | ||||
-rw-r--r-- | src/lib/kdf/hkdf/info.txt | 2 | ||||
-rw-r--r-- | src/tests/data/hkdf_label.vec | 24 | ||||
-rw-r--r-- | src/tests/test_kdf.cpp | 38 |
5 files changed, 124 insertions, 2 deletions
diff --git a/src/lib/kdf/hkdf/hkdf.cpp b/src/lib/kdf/hkdf/hkdf.cpp index 20215125b..d66847244 100644 --- a/src/lib/kdf/hkdf/hkdf.cpp +++ b/src/lib/kdf/hkdf/hkdf.cpp @@ -1,6 +1,6 @@ /* * HKDF -* (C) 2013,2015 Jack Lloyd +* (C) 2013,2015,2017 Jack Lloyd * (C) 2016 René Korthaus, Rohde & Schwarz Cybersecurity * * Botan is released under the Simplified BSD License (see license.txt) @@ -73,4 +73,50 @@ size_t HKDF_Expand::kdf(uint8_t key[], size_t key_len, return offset; } +secure_vector<uint8_t> +hkdf_expand_label(const std::string& hash_fn, + const uint8_t secret[], size_t secret_len, + const std::string& label, + const uint8_t hash_val[], size_t hash_val_len, + size_t length) + { + if(length > 0xFFFF) + throw Invalid_Argument("HKDF-Expand-Label requested output too large"); + if(label.size() > 0xFF) + throw Invalid_Argument("HKDF-Expand-Label label too long"); + if(hash_val_len > 0xFF) + throw Invalid_Argument("HKDF-Expand-Label hash too long"); + + auto mac = MessageAuthenticationCode::create("HMAC(" + hash_fn + ")"); + if(!mac) + throw Invalid_Argument("HKDF-Expand-Label with HMAC(" + hash_fn + ") not available"); + + HKDF_Expand hkdf(mac.release()); + + secure_vector<uint8_t> output(length); + std::vector<uint8_t> prefix(3 + label.size() + 1); + + prefix[0] = get_byte<uint16_t>(0, length); + prefix[1] = get_byte<uint16_t>(1, length); + prefix[2] = static_cast<uint8_t>(label.size()); + + copy_mem(prefix.data() + 3, + reinterpret_cast<const uint8_t*>(label.data()), + label.size()); + + prefix[3 + label.size()] = static_cast<uint8_t>(hash_val_len); + + /* + * We do something a little dirty here to avoid copying the hash_val, + * making use of the fact that Botan's KDF interface supports label+salt, + * and knowing that our HKDF hashes first param label then param salt. + */ + hkdf.kdf(output.data(), output.size(), + secret, secret_len, + hash_val, hash_val_len, + prefix.data(), prefix.size()); + + return output; + } + } diff --git a/src/lib/kdf/hkdf/hkdf.h b/src/lib/kdf/hkdf/hkdf.h index e63973497..7d2b7f8b2 100644 --- a/src/lib/kdf/hkdf/hkdf.h +++ b/src/lib/kdf/hkdf/hkdf.h @@ -86,6 +86,20 @@ class BOTAN_PUBLIC_API(2,0) HKDF_Expand final : public KDF std::unique_ptr<MessageAuthenticationCode> m_prf; }; +/** +* HKDF-Expand-Label from TLS 1.3/QUIC +* @param label the full label (no "TLS 1.3, " or "tls13 " prefix +* is applied) +*/ +secure_vector<uint8_t> +BOTAN_PUBLIC_API(2,3) hkdf_expand_label( + const std::string& hash_fn, + const uint8_t secret[], size_t secret_len, + const std::string& label, + const uint8_t hash_val[], size_t hash_val_len, + size_t length); + + } #endif diff --git a/src/lib/kdf/hkdf/info.txt b/src/lib/kdf/hkdf/info.txt index db6618c3c..b9b17b3b8 100644 --- a/src/lib/kdf/hkdf/info.txt +++ b/src/lib/kdf/hkdf/info.txt @@ -1,3 +1,3 @@ <defines> -HKDF -> 20131128 +HKDF -> 20170927 </defines> diff --git a/src/tests/data/hkdf_label.vec b/src/tests/data/hkdf_label.vec new file mode 100644 index 000000000..925b96869 --- /dev/null +++ b/src/tests/data/hkdf_label.vec @@ -0,0 +1,24 @@ +[SHA-256] + +# Generated by the Go version in BoringSSL + +Secret = fb8e20fc2e4c3f248c60c39bd652f3c1347298bb977b8b4d5903b85055620603 +Label = TLS 1.3, something or other +HashValue = e5007b1522d10013a1a7a32b3a6f82df1861074ba3d592abc9656cef425d1e9a +Output = 531c0f2906532ece3c7c352b73885a87558d6b2f + +Secret = 4e26e6cc23cff7773690e838ed16648b4143d200b5835e6f38525055e0f71cb2 +Label = TLS 1.3, something or other +HashValue = 16ff13612414de6dd55a976d355418756129a69eeaf116b807f6dd +Output = 1f55b71137985032969620eb7f598694ae16f490 + +[SHA-384] +Secret = 7fff5f3d5a1637d91e5e27290b6a0f95d3be48f29a82433dae0e4db1a77176b19143076fc35f0217a16939ea6a8dfe7c +Label = TLS 1.3, something or other +HashValue = 55acb630845700a42a5e5d41dd2f7816b5599d949ed86674eb26644b594bc34443f1cd095d168377cabb7b +Output = 831a9592e0349f7abba2dff78af07418d489f353 + +Secret = 47115775423d78074b1a1e605a941836f462a17c865c4e97efe3355a445710087af4010ed1f570e34a7f31aca3b060e7d2d27bf5dc902327b90d05 +Label = TLS 1.3, now that's what I call a label! +HashValue = ff4351c0de4ca54560d26cb5bdd4048fed3853297ebb516b284ccfd5e081ce5c0719965323f65914516669 +Output = e4b8ca51ee3567db6fe6fd5e656568140dbee604abe9e9e92a24e885c313a070 diff --git a/src/tests/test_kdf.cpp b/src/tests/test_kdf.cpp index 775f5b5ac..c85f2880f 100644 --- a/src/tests/test_kdf.cpp +++ b/src/tests/test_kdf.cpp @@ -10,6 +10,10 @@ #include <botan/kdf.h> #endif +#if defined(BOTAN_HAS_HKDF) + #include <botan/hkdf.h> +#endif + namespace Botan_Tests { namespace { @@ -55,6 +59,40 @@ BOTAN_REGISTER_TEST("kdf", KDF_KAT_Tests); #endif +#if defined(BOTAN_HAS_HKDF) +class HKDF_Expand_Label_Tests final : public Text_Based_Test + { + public: + HKDF_Expand_Label_Tests() : + Text_Based_Test("hkdf_label.vec", "Secret,Label,HashValue,Output") {} + + Test::Result run_one_test(const std::string& hash_name, const VarMap& vars) override + { + Test::Result result("HKDF-Expand-Label(" + hash_name + ")"); + + const std::vector<uint8_t> secret = get_req_bin(vars, "Secret"); + const std::vector<uint8_t> hashval = get_req_bin(vars, "HashValue"); + const std::string label = get_req_str(vars, "Label"); + const std::vector<uint8_t> expected = get_req_bin(vars, "Output"); + + Botan::secure_vector<uint8_t> output = + Botan::hkdf_expand_label(hash_name, + secret.data(), secret.size(), + label, + hashval.data(), hashval.size(), + expected.size()); + + result.test_eq("Output matches", output, expected); + + return result; + } + + }; + +BOTAN_REGISTER_TEST("hkdf_expand_label", HKDF_Expand_Label_Tests); + +#endif + } } |