This also doubles as HKDF implementation test.
@@ -0,0 +1,2 @@
+136,95,192,41,179,34,75,137,110,9,224,187,229,235,52,126,197,158,104,39,200,232,87,179,148,245,79,244,155,136,168,246,83,68,116,156,52,46,53,108,71,252
+Absent salt rejected! TypeError: Not an object of type BufferSource
@@ -0,0 +1,38 @@
+<!DOCTYPE html>
+<script src="include.js"></script>
+<script>
+ asyncTest(async done => {
+ var subtle = self.crypto.subtle;
+ var key = await subtle.importKey("raw", new Uint8Array([]), { name: "HKDF" }, false, [
+ "deriveKey",
+ "deriveBits",
+ ]);
+ // Test the subtle difference between an empty salt (0 bytes) and an absent salt (which defaults to hashLen many bytes, each of value 0).
+ // Inspired by https://datatracker.ietf.org/doc/html/rfc5869/#appendix-A.6 and A.7
+ var algorithmEmptySalt = {
+ name: "HKDF",
+ salt: new Uint8Array([]),
+ info: new Uint8Array([]),
+ hash: "SHA-1",
+ };
+ var bitsEmptySalt = await subtle.deriveBits(algorithmEmptySalt, key, 42 * 8);
+ console.log(new Uint8Array(bitsEmptySalt));
+ println(new Uint8Array(bitsEmptySalt));
+ var algorithmAbsentSalt = {
+ salt: null,
+ subtle.deriveBits(algorithmAbsentSalt, key, 42 * 8).then(
+ async bitsAbsentSalt => {
+ println(new Uint8Array(bitsAbsentSalt));
+ done();
+ },
+ async err => {
+ println("Absent salt rejected! " + err);
+ }
+ );
+ });
+</script>
@@ -1467,7 +1467,10 @@ WebIDL::ExceptionOr<JS::NonnullGCPtr<JS::ArrayBuffer>> HKDF::derive_bits(Algorit
// * the contents of the salt member of normalizedAlgorithm as salt,
// * the contents of the info member of normalizedAlgorithm as info,
// * length divided by 8 as the value of L,
- // FIXME: salt null versus salt empty?!
+ // Note: Although HKDF technically supports absent salt (treating it as hashLen many NUL bytes),
+ // all major browsers instead raise a TypeError, for example:
+ // "Failed to execute 'deriveBits' on 'SubtleCrypto': HkdfParams: salt: Not a BufferSource"
+ // Because we are forced by neither peer pressure nor the spec, we don't support it either.
auto const& hash_algorithm = TRY(normalized_algorithm.hash.visit(
[](String const& name) -> JS::ThrowCompletionOr<String> { return name; },
[&](JS::Handle<JS::Object> const& obj) -> JS::ThrowCompletionOr<String> {