<div dir="ltr">Alex asked me about how I extended the Noise-C API to deal with Noise Pipes, and I thought I might share the approach with the rest of the mailing list. This is my contribution to how we might handle other types of fallback protocols should we choose to do that.<br><div><br></div><div>The specification effectively treats the IK and XXfallback phases of Noise Pipes as separate: the application sees the failure and then starts a completely new handshake, hand-carrying across the static and ephemeral keys to satisfy the XXfallback requirements.<br></div><div><br></div><div>I instead took a different approach: the HandshakeState for IK is converted into one for XXfallback, within the same object. At the high level, I have a Fallback() method that does the following:<br><div><br></div><div>Fallback():<br></div><div> - Change the pattern in the protocol name to "XXfallback".<br></div><div> - Clear the remote static public key.<br></div><div> - Clear the responder's ephemeral key but keep the initiator's ephemeral key.<br></div><div> - Swap the initiator/responder role of the handshake.<br></div><div> - Call Initialize() again.<br></div><div><br></div><div>All
other parameters (prologue, psk, cipher, dh, hash, s) remain the same. In a previous version of Noise-C, the prologue and psk had to be provided again as arguments to Fallback() because they had been forgotten by that point in the handshake. The latest Noise-C remembers those values too, but alternative implementations don't have to. Note: Noise-C does provide a way to override the previous parameters before Initialize() if the application needs to, but it usually doesn't.<br><br></div><div>With this API, the local static key and initiator's ephemeral key do not need to be provided again because they are already in the HandshakeState.<br><br>This API doesn't provide a complete implementation of Noise Pipes - it only provides a primitive to make the application's life easier. There are still the issues of packet types and framing for the application to deal with.<br></div><div><br></div><div>This method could conceivably be extended to other kinds of fallbacks. For each pair of before/after patterns, the Fallback() method performs a sequence of actions to convert one into the other while leaving the bulk of the settings the same.<br><br></div><div>I can see two possible approaches to generalize this: fallback tokens and pre-message analysis.<br><br></div><div>Fallback tokens would be similar to message tokens, but instead define the primitive actions to perform for a specific before/after pattern pair. For example, "clear-rs", "swap-roles", "keep-init-e", ... The downside of this is the O(n^2) nature - each potential pair needs a fallback token sequence defined.<br><br></div><div>The other approach is pre-message analysis: look at the arguments and pre-messages for the new pattern. Clear any key that is no longer required, and check that required keys are already present in the HandshakeState. Each requirement is dealt with separately with a keep/clear decision for each.<br><br></div><div>Pre-message analysis would allow fallbacks between lots of existing patterns. For example, it is possible to fallback from IK to NN by throwing away all keys and starting again from scratch with no identity information. The value of this is debatable, so we may want to restrict fallbacks to only those patterns that start with "<- e" and have matching static key requirements [both IK and XXfallback have (s, rs) in their arguments].<br><br></div><div>Long story short: I think fallbacks can be made more general by introducing a formal Fallback() method into the HandshakeState with defined semantics as to how the implementation interprets an invocation of Fallback().<br></div><div><br></div><div>Cheers,<br><br></div><div>Rhys.<br><br></div></div></div>