Code change:
Based on discussions with NAME NAME and others, there are potential key confusion attacks when the server is checking the signature using the wrong public key. I think the simplest solution is to add the public key to the TLS key exporter. That should solve the issue without increasing the amount of bytes sent on the wire
Including the public key in the exporter context makes it significantly harder to implement the server side of this. Including the hash of the public key in the signature appears to work also. I have a sketch model in Tamarin that doesn't find any attacks when the hash of the public key is in the signature, which gives me some confidence that it's doable, although NAME wasn't convinced. We should discuss this in the room at 117.
I agree with NAME The implementation complexity that comes from putting the public key in the exporter context is real, at least for some edge networks. I think we should pursue alternatives that don't weaken the security properties, even if they make the registration step a bit more explicit.
If this design makes implementation hard then we should totally change it. Can I ask you to elaborate on what causes this to be hard on the server side?
The basic problem in our particular case is that the component that terminates TLS and computes exported values is upstream of the component that has the public key. So when a request comes in with the header to the TLS terminator, the public key needs to get relayed back for computation. If, instead, the public key was signed, then the TLS terminator could simply forward the computed exporter without any sort of round trip. (I punched this in on my phone, but can draw pictures on a laptop if the above is not clear.) We don't necessarily need to cater to one implementation, but I would be surprised if this were not a common arrangement in practice.
Thanks, that makes sense. To explore the problem space: if we were to include the public key in both the context and a header parameter, would that work for your implementation? (That way the TLS terminator can call the exporter without a round trip.)
Yep, that would address the problem too.
SIGMA seems like it might be worth considering as an alternative. That is, sign the nonce, but then include a MAC that covers all of the relevant parameters.
Something like SIGMA should also work, but I'd like to minimize the number of primitives required to implement this. Currently, modifying an HTTPS stack to support this draft only requires a signature implementation and a TLS key exporter API. Using a MAC would require a MAC primitive and opens the question "which MAC should we use?"
That's an easy question: you are using HKDF to get the values, so HMAC is a logical choice. The only reason I suggested that is that I'm increasingly nervous about these bespoke constructions. We understand what sign and mac provides.
I think we need to first identify what property it is we actually want, and then see what is the simplest way to get there. NAME what is the property we want?
NAME had a long chat about this today. We didn't come to any solid conclusions - the authentication property here is really subtle and the attacker model not yet clear. This is going to need some really careful work and detailed formal analysis to get right. In terms of this discussion, my personal take would be: I don't think a mac & sign approach is going to give you anything strictly better or different than an exporter + signature approach. In both cases its just a question of what you put in the exporter / mac. Having the public key conveyed in the header and bound explicitly in the exporter (rather than via the keyid) seems like a conservative and reasonable approach.
Thanks for thinking through this! I've written up to add the public key to both the context and the header

Related discussion:
text: 5.2. <ins> The REQUIRED "a" (public key) parameter is a byte sequence that contains the public key used by the server to validate the signature provided by the client.  This avoids key confusion issues (see SEEMS- LEGIT).  The encoding of the public key is described in context. 5.3. </ins> The REQUIRED "p" (proof) parameter is a byte sequence that specifies the proof that the user agent provides to attest to possessing the credential that matches its key ID. <del> 5.3. </del> <ins> 5.4. </ins> The REQUIRED "s" (signature) parameter is an integer that specifies the signature scheme used to compute the proof transmitted in the "p"