aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/lib/kdf/hkdf/hkdf.cpp48
-rw-r--r--src/lib/kdf/hkdf/hkdf.h14
-rw-r--r--src/lib/kdf/hkdf/info.txt2
-rw-r--r--src/tests/data/hkdf_label.vec24
-rw-r--r--src/tests/test_kdf.cpp38
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
+
}
}