Code change:
Initial attempt to solve . This defines the KDF as discussed in and relocates the DH computation obligation and verification as part of the Derive-Key-Pair function definition.
Fixing my own review comments here, since NAME appears to be offline and we need to publish -04.
The PR from NAME introducing a KDF was merged before I had a chance to review it. There are a number of problems with it: Instead of calling for an abstract KDF, it should define one, as we have with the use of HKDF elsewhere We can't just say , since KDFs produce octet strings, not private keys. We need , not just . This doesn't match what I recall discussing on the list. I think what you want is actually as follows: Or in prose:
That was exactly the question I was asking on the list, i.e., if we need an explicit "derive-keypair" function. The idea with deriving the private key directly was to avoid a redundant derive-key operation. I'm totally fine re-doing the PR to represent either approach.
Merging that PR too quickly is on me. For the parent derivation, we can just use what the formal model for TreeKEM does, which is roughly the Richard points out. The part I don't like about this, is to define in the MLS specification as it depends on the HPKE internal algorithm. The TreeKEM formal specification, it is done the following way (because we know that the HPKE interface we use can take random bytes as a secret key): In the MLS specification, I suggest we avoid defining anything new but point to the HPKE spec instead. As it provides the KEM scheme, it should also provide ways to derive the from an octet string and the function that transform an to a . I suggest we define, in the HPKE document, something like that we already need anyway and eventually that maps the random bytes to an . For X25519 we don't even need the second function but generically we probably need it. That would lead to define the following in the MLS spec: NAME ?
I don't think we can delegate this to HPKE. HPKE doesn't have a need for anything like Derive-Key-Pair. We do, so it's on us to define it. The point NAME raises about avoiding unnecessary operations is relevant here: If we're forking the hashes like this, then we can remove the hashing part from Derive-Key-Pair, so that it's really just a definition of how you convert from an octet string to a private key (and thus its public key).
I disagree, it is not for MLS to define the and functions for all asymmetric schemes possibly used in HPKE. In MLS we should have to do only what TreeKEM does, aka, 2 KDFs, one outputing the parent's secret, one outputing the KEM encryption key, and that's it. I am willing to accept defining here but this is already odd.
If it's not our job, whose is it? It's not HPKE's job, because they don't have a need to convert byte strings to private keys. Some document needs to define this conversion in order for MLS to be implementable.
I agree we are in trouble here :) But I still wish that if we can avoid it, we should.
I agree that the KEM schemes themselves should define these things, like Curve25519 does. But it's like two lines of text. I'm not too bothered with it.
I think I'm a bit lost here. How do we go forward? Regarding the use of an abstract KDF: I was mostly just adapting the existing text, which used an abstract hash function if I recall correctly. I didn't touch the texts that got more concrete because they seemed to be a little outdated anyway. That shouldn't be a problem though.
NAME Ok, looking at this now, I am not sure what we decided in the end. I would be ok to define as you said but as it is KEM specific so there is no good solution for P256 or for whatever PQ KEM that would have a specific way of generating keys from pseudo random bytes...
NAME any suggestion here
Fixed by

Related discussion:
text: An asymmetric public key <del> The private key for a node is derived from its secret value using the KDF.  The public key is then derived from the private key using the Derive-Public-Key function. </del> <ins> The contents of the parent are based on the latest-updated child. Nodes in a tree are always updated along the "direct path" from a leaf to the root.  The generator of the update chooses a random secret value "path_secret[0]", and generates a sequence of "path secrets", one for each node from the leaf to the root.  That is, path_secret[0] is used for the leaf, path_secret[1] for its parent, and so on.  At each step, the path secret is used to derive a new secret value for the corresponding node, from which the node's key pair is derived. </ins> <del> The contents of a parent node are computed from one of its children as follows: </del> <ins> For example, suppose there is a group with four participants: </ins> <del> The contents of the parent are based on the latest-updated child. For example, if participants with leaf secrets A, B, C, and D join a group in that order, then the resulting tree will have the following structure: </del> <ins> If the first participant subsequently generates an update based on a secret X, then the sender would generate the following sequence of path secrets and node secrets: </ins> <del> If the first participant subsequently changes its leaf secret to be X, then the tree will have the following structure. </del> <ins> After the update, the tree will have the following structure, where "ns[i]" represents the node_secret values generated as described above: </ins> 5.3.