Skip to content

Make deriveBits length parameter optional and nullable#345

Merged
twiss merged 1 commit into
mainfrom
optional-deriveBits-length
Jun 26, 2024
Merged

Make deriveBits length parameter optional and nullable#345
twiss merged 1 commit into
mainfrom
optional-deriveBits-length

Conversation

@twiss

@twiss twiss commented May 8, 2023

Copy link
Copy Markdown
Member

Fixes #322, fixes #329. This is marked as a draft since there is not yet consensus in those issues that this is the solution we should go with, but having a concrete proposal might help move the process along.


This change allows omitting the length parameter from calls to deriveBits, defaulting to null, and also allows passing null explicitly (as the web platform tests already do).

The "derive bits" operations already handle null as it can also be returned by the "get key length" operations.

In the case of ECDH, the operation returns the entire derived key; in the case of HKDF and PBKDF2, the operation returns an error.

This is technically speaking a breaking change, since currently passing null explicitly should cause it to be converted to 0, causing an empty ArrayBuffer to be returned. However, the only implementation that actually does so (Chromium) is willing to change this. Additionally, returning the entire value (for ECDH) seems more expected and more useful than returning an empty value.


Preview | Diff

Allow omitting the `length` parameter from calls to `deriveBits`,
defaulting to `null`, and also allow passing `null` explicitly
(as the web platform tests already do).

The "derive bits" operations already handle `null` as it can also
be returned by the "get key length" operations.

In the case of ECDH, the operation returns the entire derived key;
in the case of HKDF and PBKDF2, the operation returns an error.

This is technically speaking a breaking change, since currently
passing `null` explicitly should cause it to be converted to `0`,
causing an empty `ArrayBuffer` to be returned. However, the only
implementation that actually does so (Chromium) is willing to
change this. Additionally, returning the entire value (for ECDH)
seems more expected and more useful than returning an empty value.
@twiss twiss force-pushed the optional-deriveBits-length branch from be453c6 to 2b3bca8 Compare May 8, 2023 17:26
@javifernandez

Copy link
Copy Markdown
Collaborator

What else is pending to merge this PR ?

As far as I understand we have reached an agreement in #322 about the use of ` optional unsigned long and the addition of ```?=null``` would avoid the need to introducing changes in the algorithm's steps.

@twiss

twiss commented May 31, 2023

Copy link
Copy Markdown
Member Author

@javifernandez since #324 was arguably merged prematurely, I wanted to make doubly sure that we have consensus on this before it's merged :)

@annevk, @martinthomson and @saschanaz, it would be great if you could confirm whether you're OK with this solution, as I think you were at one point arguing against changing the WebIDL in #322. Though note that this PR also addresses #329, and that's not possible without changing the WebIDL - but also there we haven't discussed much whether it's worth it, and in scope for the "maintenance" charter. I do personally think this would be a nice change for developer ergonomics, but arguably it's debatable whether that's in scope. Let me know what you think!

@annevk

annevk commented Aug 17, 2023

Copy link
Copy Markdown
Member

I'm not against changing Web IDL, but I still don't see the point in allowing null. The argument being optional already gives you the value space you need.

@twiss

twiss commented Aug 17, 2023

Copy link
Copy Markdown
Member Author

@annevk It's not so much that I think allowing null is useful for new applications (that indeed can just omit the parameter), it's rather that given the question of what to do if an existing application does pass null, returning the entire value seems safer than returning an empty Uint8Array.

It's admittedly unlikely any existing web app does so since only Safari behaves "as expected" (per this PR, not per the current spec) in that case, but perhaps not entirely impossible since the web platform tests do test for it, so someone may have gotten the idea from there (or a loose reading of the current spec), and written a Safari-only application that depends on this, perhaps.

Also note that if such code would run today on Chrome, it would silently return an empty value, potentially leading to a security issue. So this change would fix a security issue in such a web app, whereas changing Safari to return the empty value instead of the full value could actually cause a security issue instead (strictly speaking not by fault of Safari, but nevertheless it seems better to be cautious here).

@twiss twiss marked this pull request as ready for review August 17, 2023 15:25
@annevk

annevk commented Aug 17, 2023

Copy link
Copy Markdown
Member

I'm personally not really concerned about that. I'll defer to @martinthomson and @davidben as to whether to accept this PR as-is.

@davidben

davidben commented Aug 17, 2023

Copy link
Copy Markdown
Collaborator

I think have a convenient way to return an untruncated value is good. Better would have been for WebCrypto to not jam Diffie-Hellman and KDFs into the same function, but I don't have a time machine. Thus, I like making it optional to mean default size.

However, the discussion in #322 (comment) suggests that undefined, not null, is the idiomatic JavaScript pattern for an optional value. Given this is not idiomatic, and given both Chromium's and the spec's long-standing behavior, I'm also not convinced by the concern that existing websites might be relying null or zero being interpreted as default.

Thus, I think idiomatic patterns should win out and we should not have the = null business. I'm also a little surprised at this being an IDL-only change... it seems we ought to have some supporting prose, but it's been a while since I'd looked carefully at the spec formulation. That does, AIUI, preserve the existing behavior of coercing null to zero, but type coercion is just deeply part of this language and trying to work around it in every function that takes a number is just going to leave a confusing, inconsistent mess.

@saschanaz

saschanaz commented Aug 17, 2023

Copy link
Copy Markdown
Member
(Wrong argument)

It's not so much that I think allowing null is useful for new applications (that indeed can just omit the parameter), it's rather that given the question of what to do if an existing application does pass null, returning the entire value seems safer than returning an empty Uint8Array.

I'm not sure I understand the argument here, passing null for optional unsigned long length will behave like passing undefined, not 0. So I still don't see why it should be nullable.

@twiss

twiss commented Aug 17, 2023

Copy link
Copy Markdown
Member Author

@davidben:

and given both Chromium's and the spec's long-standing behavior

Are you concerned about changing Chromium's behavior because you're worried that someone might be relying on passing null returning an empty value? Or some other reason?

(IMHO we shouldn't be worried about "the spec's long-standing behavior" because the spec's intention on this point is debatable, as evidenced by the web platform tests.)

However, the discussion in #322 (comment) suggests that undefined, not null, is the idiomatic JavaScript pattern for an optional value.

Would you be happier if it said optional unsigned long? length = undefined, and then the spec prose said "if length is undefined or null"?

I'm also a little surprised at this being an IDL-only change... it seems we ought to have some supporting prose, but it's been a while since I'd looked carefully at the spec formulation.

The reason it's an IDL-only change is because the prose saying "if length is null" is already there.

That does, AIUI, preserve the existing behavior of coercing null to zero, but type coercion is just deeply part of this language and trying to work around it in every function that takes a number is just going to leave a confusing, inconsistent mess.

In general I would agree but I think we should make an exception for security concerns 😅


@saschanaz:

I'm not sure I understand the argument here, passing null for optional unsigned long length will behave like passing undefined, not 0.

Are you sure? https://webidl.spec.whatwg.org/#es-overloads only mentions special behavior of undefined for optional arguments, not null. See also the references in #322 (comment).

@saschanaz

Copy link
Copy Markdown
Member

(Me quickly checking with new DOMException(null)) Ah yes, you are right, I guess I have long had a wrong understanding about optional.

@martinthomson

Copy link
Copy Markdown
Member

I'm OK with this outcome. Like @davidben, this is a consequence of deeper structural issues, but the way this works seems fine.

I'm not 100% on the outcome for length == 0. ECDH seems to return an empty array in that case, but HKDF and PBKDF2 throw. That seems wrong, at least for ECDH.

@twiss

twiss commented Aug 17, 2023

Copy link
Copy Markdown
Member Author

Right, we could throw for length == 0 as well, that seems like a reasonable protection mechanism.

@davidben

Copy link
Copy Markdown
Collaborator

I'm not 100% on the outcome for length == 0. ECDH seems to return an empty array in that case, but HKDF and PBKDF2 throw. That seems wrong, at least for ECDH.

Ah, fun. I think we should treat that separately, as it's not related to how to handle the default.

I really don't like making 0 silently turn into default, but I feel less strongly about empty vs throw. TBH, I probably would have picked consistently empty over consistently throw, but shrug. I'm not opposed to trying to align that, though as it'd be a breaking change, we should get some metrics for that.

Keep in mind that, say, 1-byte or 2-byte output is also insecure, so throwing on zero is not actually meaningfully enforcing any kind of secure use of the algorithm. Indeed for ECDH, truncation at all is a bad idea. The whole output should be passed to a KDF. Given we've already gone down the truncation route, it's just a question of whether you believe "truncate to zero" is a defined operation.

If we want to throw, we should instead be seeing whether we can compatibly throw on any truncation at all.

@davidben

davidben commented Aug 17, 2023

Copy link
Copy Markdown
Collaborator

(IMHO we shouldn't be worried about "the spec's long-standing behavior" because the spec's intention on this point is debatable, as evidenced by the web platform tests.)

We've discussed this thoroughly in #322 already, but the WPTs came long after the spec and original Chromium implementation. The spec was also originally written in large part by someone from Chromium. Whether or not the intended semantics were right (like I said, I'm extremely unhappy with how the spec handles ECDH overall), it's quite clear what the spec's intent was.

@martinthomson

martinthomson commented Aug 17, 2023

Copy link
Copy Markdown
Member

FWIW, 0 = empty seems obvious but it's a nonsensical thing to ask for, so I'm OK with throwing. A separate PR might be needed there.

Edit: I agree with David about truncation for ECDH, at any number of bytes. But I have found many uses for very short KDF outputs.

@twiss

twiss commented Aug 17, 2023

Copy link
Copy Markdown
Member Author

Ah, fun. I think we should treat that separately, as it's not related to how to handle the default.

Yeah, I'll make a separate PR for that.

Keep in mind that, say, 1-byte or 2-byte output is also insecure

Sure, I meant more that it could be a protection mechanism against accidentally getting an empty value instead of the full value. For example, Safari currently returns the full value when returning 0, it might be better to change that to throw rather than returning the empty value (which could theoretically cause a security issue).

If we want to throw, we should instead be seeing whether we can compatibly throw on any truncation at all.

Can we not let perfect be the enemy of good? 😅 We already know we can compatibly throw for 0, as Firefox does so, so it would seem reasonable to me to start with that.

@davidben

davidben commented Aug 17, 2023

Copy link
Copy Markdown
Collaborator

If we want to throw, we should instead be seeing whether we can compatibly throw on any truncation at all.

Can we not let perfect be the enemy of good? 😅 We already know we can compatibly throw for 0, as Firefox does so, so it would seem reasonable to me to start with that.

The tradeoff is between trying to enforce some security criteria (which rejecting zero does a bad job of) vs having an extra case of fallibility in the function. The point of my comment isn't striving towards perfection, but that we're sacrificing continuity of semantics for a security check that doesn't actually do much. The continuity doesn't mean much, but measured against a security check that doesn't do much, this is all silly.

Since starting to throw would be a spec-incompatible change, and a breaking change for Chromium, we'd need some code to measure things. If we're measuring things anyway, we should also validate whether we can just remove this truncation behavior altogether, as it was wrong to begin with.

@twiss

twiss commented Aug 17, 2023

Copy link
Copy Markdown
Member Author

(I've created #351 for this)

The tradeoff is between trying to enforce some security criteria (which rejecting zero does a bad job of)

Again, the security check wouldn't be to protect web applications in general, just any code that happens to rely on WebKit returning the entire value for zero. Rejecting zero does do a good job of protecting against that 🙃

Since starting to throw would be a spec-incompatible change, and a breaking change for Chromium, we'd need some code to measure things. If we're measuring things anyway, we should also validate whether we can just remove this truncation behavior altogether, as it was wrong to begin with.

But yes, fair enough. If you do want to do some measurements that would be great, let us know what the outcome is 😊

@davidben

Copy link
Copy Markdown
Collaborator

Ah fair, yeah zero is a bit special because of this mess. :-)

@twiss

twiss commented Aug 17, 2023

Copy link
Copy Markdown
Member Author

@martinthomson and @saschanaz could you please explicitly approve this PR, if you do indeed approve of it (with or without #351)?

And @davidben I'll repeat this part as it was a genuine question:

Would you be happier if it said optional unsigned long? length = undefined, and then the spec prose said "if length is undefined or null"?

And perhaps if everyone else wants to weigh in on that alternative option that'd be great 😊

@martinthomson

Copy link
Copy Markdown
Member

I really don't think that = undefined changes anything in this case.

@davidben

davidben commented Aug 17, 2023

Copy link
Copy Markdown
Collaborator

Certainly there's no difference between = null and = undefined with prose for null. The question in my mind is whether passing in an explicit null should be interpreted as using a default.

My preference would be what was already discussed in the issue:
#322 (comment)
#322 (comment)
#322 (comment)

That is, we should just do whatever would be idiomatic on the web for an optional parameter. That seems to be plain optional unsigned long length, which I understand from the discussion to not permit null as a stand-in for an omitted value.

But if we're really set on ignoring convention for some reason, I can live with null being a stand-in. (WebCrypto is very much not a hill worth dying on. 😛) But then there should be a clear reason for it, and it doesn't seem like there is one, beyond the WPTs which were always just a mistake in the tests.

@javifernandez

Copy link
Copy Markdown
Collaborator

Oh I see, yeah makes sense. So you want to close #351, then?

I think we can do that later, once we manage to solve the lack of interoperability of the deriveOperations in several algorithms, I think returning an empty string when length is 0 is consistent with the spec and what it's already implemented in some engines.

If we want to change the spec of a specific algorithm to raise an exception and forbid that case, we may discuss it independently of this PR.

@javifernandez

Copy link
Copy Markdown
Collaborator

Hi @annevk, would you mind to explicitly approve the change on behalf of Safari now that Chrome and Firefox have ?

@annevk annevk left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure.

@twiss twiss merged commit c0e1856 into main Jun 26, 2024
@twiss twiss deleted the optional-deriveBits-length branch June 26, 2024 21:43
@twiss

twiss commented Jun 26, 2024

Copy link
Copy Markdown
Member Author

Thanks, everyone!

github-actions Bot added a commit that referenced this pull request Jun 26, 2024
SHA: c0e1856
Reason: push, by twiss

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
littledivy pushed a commit to denoland/deno that referenced this pull request Jul 4, 2024
zebreus pushed a commit to zebreus/deno that referenced this pull request Jul 8, 2024
webkit-commit-queue pushed a commit to javifernandez/WebKit that referenced this pull request Jul 23, 2024
https://bugs.webkit.org/show_bug.cgi?id=276394

Reviewed by Youenn Fablet and Nitin Mahendru.

The PR#345 [1] to the WebCryptoAPI spec defines now the 'length'
parameter as optional, defaulting to 'null'. This change tries to
solve a long-standing interoperability issue in the deriveBits
operation.

This patch implements the required changes in the IDL so that the
'length' parameter is declared as optional, with 'null' as default
value when omitted. The affected algorithms (ECDH, HKDF, PBKDF2
and X25519) are adapted to the parameter's new type.

The PR#43400 [2] defined tests for the new behavior of the afected
algorithms, which they all pass now.

[1] w3c/webcrypto#345
[2] web-platform-tests/wpt#43400

* LayoutTests/crypto/subtle/derive-bits-malformed-parameters-expected.txt:
* LayoutTests/crypto/subtle/derive-bits-malformed-parameters.html:
* LayoutTests/imported/w3c/web-platform-tests/WebCryptoAPI/derive_bits_keys/derived_bits_length.https.any-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/WebCryptoAPI/derive_bits_keys/derived_bits_length.https.any.worker-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/WebCryptoAPI/idlharness.https.any-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/WebCryptoAPI/idlharness.https.any.worker-expected.txt:
* LayoutTests/imported/w3c/web-platform-tests/interfaces/WebCryptoAPI.idl:
* Source/WebCore/crypto/CryptoAlgorithm.cpp:
(WebCore::CryptoAlgorithm::deriveBits):
* Source/WebCore/crypto/CryptoAlgorithm.h:
* Source/WebCore/crypto/SubtleCrypto.cpp:
(WebCore::SubtleCrypto::deriveKey):
(WebCore::SubtleCrypto::deriveBits):
* Source/WebCore/crypto/SubtleCrypto.h:
* Source/WebCore/crypto/SubtleCrypto.idl:
* Source/WebCore/crypto/algorithms/CryptoAlgorithmECDH.cpp:
(WebCore::CryptoAlgorithmECDH::deriveBits):
* Source/WebCore/crypto/algorithms/CryptoAlgorithmECDH.h:
* Source/WebCore/crypto/algorithms/CryptoAlgorithmHKDF.cpp:
(WebCore::CryptoAlgorithmHKDF::deriveBits):
* Source/WebCore/crypto/algorithms/CryptoAlgorithmHKDF.h:
* Source/WebCore/crypto/algorithms/CryptoAlgorithmPBKDF2.cpp:
(WebCore::CryptoAlgorithmPBKDF2::deriveBits):
* Source/WebCore/crypto/algorithms/CryptoAlgorithmPBKDF2.h:
* Source/WebCore/crypto/algorithms/CryptoAlgorithmX25519.cpp:
(WebCore::CryptoAlgorithmX25519::deriveBits):
* Source/WebCore/crypto/algorithms/CryptoAlgorithmX25519.h:

Canonical link: https://commits.webkit.org/281240@main
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request Aug 2, 2024
…,webidl,smaug DONTBUILD

The PR#345 [1] of the WebCrypto API specification changed the type
of the deriveBits's length argument to become 'optional' and with
'null' as default value.

The affected WebCrypto algorithms (HKDF, PBKDF2, ECDH and X25519)
will be adapted to handle the case of a null length properly.

[1] w3c/webcrypto#345

Differential Revision: https://phabricator.services.mozilla.com/D217532
jamienicol pushed a commit to jamienicol/gecko that referenced this pull request Aug 5, 2024
…,webidl,smaug DONTBUILD

The PR#345 [1] of the WebCrypto API specification changed the type
of the deriveBits's length argument to become 'optional' and with
'null' as default value.

The affected WebCrypto algorithms (HKDF, PBKDF2, ECDH and X25519)
will be adapted to handle the case of a null length properly.

[1] w3c/webcrypto#345

Differential Revision: https://phabricator.services.mozilla.com/D217532
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request Aug 7, 2024
…,webidl,smaug

The PR#345 [1] of the WebCrypto API specification changed the type
of the deriveBits's length argument to become 'optional' and with
'null' as default value.

The affected WebCrypto algorithms (HKDF, PBKDF2, ECDH and X25519)
will be adapted to handle the case of a null length properly.

[1] w3c/webcrypto#345

Differential Revision: https://phabricator.services.mozilla.com/D217532
aosmond pushed a commit to aosmond/gecko that referenced this pull request Aug 8, 2024
…,webidl,smaug

The PR#345 [1] of the WebCrypto API specification changed the type
of the deriveBits's length argument to become 'optional' and with
'null' as default value.

The affected WebCrypto algorithms (HKDF, PBKDF2, ECDH and X25519)
will be adapted to handle the case of a null length properly.

[1] w3c/webcrypto#345

Differential Revision: https://phabricator.services.mozilla.com/D217532
jwidar pushed a commit to jwidar/LatencyZeroGithub that referenced this pull request Sep 16, 2025
…,webidl,smaug DONTBUILD

The PR#345 [1] of the WebCrypto API specification changed the type
of the deriveBits's length argument to become 'optional' and with
'null' as default value.

The affected WebCrypto algorithms (HKDF, PBKDF2, ECDH and X25519)
will be adapted to handle the case of a null length properly.

[1] w3c/webcrypto#345

Differential Revision: https://phabricator.services.mozilla.com/D217532
jwidar pushed a commit to jwidar/LatencyZeroGithub that referenced this pull request Sep 16, 2025
…,webidl,smaug

The PR#345 [1] of the WebCrypto API specification changed the type
of the deriveBits's length argument to become 'optional' and with
'null' as default value.

The affected WebCrypto algorithms (HKDF, PBKDF2, ECDH and X25519)
will be adapted to handle the case of a null length properly.

[1] w3c/webcrypto#345

Differential Revision: https://phabricator.services.mozilla.com/D217532
Jarred-Sumner pushed a commit to oven-sh/bun that referenced this pull request Jun 28, 2026
…32819)

Fixes #12343

### Problem

`crypto.subtle.deriveBits(algorithm, baseKey, 0)` returns the entire
ECDH / X25519 shared secret instead of zero bits:

```js
const kp = await crypto.subtle.generateKey({ name: "ECDH", namedCurve: "P-256" }, false, ["deriveBits"]);
const bits = await crypto.subtle.deriveBits({ name: "ECDH", public: kp.publicKey }, kp.privateKey, 0);
bits.byteLength; // Bun: 32 (the whole secret)   Node / spec: 0
```

The spec declares the argument as `optional unsigned long? length =
null` (changed in w3c/webcrypto#345, which #12343 tracks). Bun's binding
converts it as a required non-nullable `unsigned long`, so `null`
collapses to `0`, and the algorithm implementations then use `length ==
0` as the "return everything" sentinel. Any wrapper that computes
`length` dynamically and can reach `0` silently receives the raw shared
secret. The same sentinel causes three more divergences (all
cross-checked against Node v26):

| call | Bun | Node / spec |
|---|---|---|
| `deriveBits(ecdh, key, 0)` | full 32-byte secret | empty `ArrayBuffer`
|
| `deriveBits(ecdh, key)` (length omitted) | `TypeError: Not enough
arguments` | full secret |
| `deriveBits(hkdf, key, 0)`, `deriveBits(pbkdf2, key, 0)` |
`OperationError` | empty `ArrayBuffer` |
| `deriveBits(ecdh, key, n)` with `n % 8 != 0` | last byte not masked |
unused trailing bits zeroed |

The last row is also security-relevant: a non-byte-aligned length
returns more secret bits than requested. With a fixed P-256 pair,
`deriveBits(..., 255)` gives `...6574a17b` on Bun and `...6574a17a` on
Node (`0x7b & 0xfe`).

### Fix

Backport the current WebKit signatures so the length stays nullable end
to end: `SubtleCrypto::deriveBits` and `CryptoAlgorithm::deriveBits`
take `std::optional<size_t>`, and `getKeyLength` returns
`ExceptionOr<std::optional<size_t>>` (HKDF and PBKDF2 report "no
inherent length" as `nullopt` instead of `0`). The binding converts the
argument with `IDLNullable<IDLUnsignedLong>`, so `null`, `undefined`,
and an omitted argument all become `nullopt` while `0` stays `0`.
`deriveBits.length` changes from 3 to 2 accordingly.

The ECDH and X25519 truncation now goes through one shared helper,
`CryptoAlgorithm::extractDerivedBits`, which also zeroes the unused
trailing bits of the final byte (`last &= 0xFF << (8 - length % 8)`).
Upstream WebKit still omits that step; Node applies it (`masks[mod]` in
`lib/internal/crypto/diffiehellman.js`) and the spec's "the first length
bits of secret" requires it.

`deriveKey` behavior is unchanged: `getKeyLength` returning `0` for
HKDF/PBKDF2 already meant "derive the whole secret", it now says so with
`nullopt`.

### Verification

- `test/js/web/crypto/web-crypto.test.ts` gains a
`SubtleCrypto.deriveBits length` block over a fixed P-256 pair. Every
expected value was produced by Node v26 against the same keys:
null/undefined/omitted/0/256/257, the masked outputs for 1/3/9/17/33/255
bits, HKDF/PBKDF2 with 0/null/7/16, PBKDF2 with a zero iteration count
at lengths 0 and 256, and a `deriveKey` round trip through a length-less
derived key type.
- `test/js/bun/crypto/x25519-derive-bits.test.ts`: the existing `"zero
length returns full output"` test asserted the old behavior; it now
asserts zero bytes. New tests cover an omitted length and trailing-bit
masking against the RFC 7748 vector.

All of the new assertions fail on `main`.
`test/js/deno/crypto/webcrypto.test.ts`,
`test/regression/issue/24399.test.ts`,
`test/js/bun/crypto/wpt-webcrypto.generateKey.test.ts`, and the eight
active `test-webcrypto-*.js` Node compat tests still pass.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Consider making the deriveBits length argument optional deriveBits length idl does not allow null

7 participants