[noise] Questions regarding the Additional Symmetric Keys extension

Trevor Perrin trevp at trevp.net
Tue Jul 10 10:23:21 PDT 2018


Hi Mathias,

Thanks for the thoughtful comments:


On Tue, Jul 10, 2018 at 2:39 PM, Mathias Hall-Andersen
<mathias at hall-andersen.dk> wrote:
>
> Why do we want to update ask_master after split?

Hmm, good question.

Part of the answer is history: we started proposing it in Split(),
then added it in MixKey/MixKeyAndHash.

A better reason: If you only care about post-handshake ASK then it's
easier to do it once in Split() and skip doing it in
MixKey/MixKeyAndHash.  Otherwise, you'd have to identify the final
MixKey/MixKeyAndHash call in the handshake, which takes a little bit
more thought.

But I'm not sure that's a compelling argument for the extra
calculations in Split().  Perhaps we should change this?

(But also: your last point - on using ck directly - might moot this question)


> We should use the full output of HKDF in "invoke_chain" rather than truncate to 32 bytes
> to accommodate the use of 512-bit hash functions with the goal that the output keys
> should be capable of serving as collision-resistant hashes of the session transcript
> (they should offer the same level of security as "h").

In cases where you want a collision-resistant hash of the session
transcript, I've proposed deriving 2 ASKs, e.g. a PSK plus a "PSK
addendum", and treating the pair of (PSK, PSK addendum) as such a
collision-resistant hash.

https://moderncrypto.org/mail-archive/noise/2018/001758.html

One thing I like about this is that the PSK addendum value doesn't
need to be treated as a secret, so could also be used as a session
identifier.  Also, you don't always need this property, so it made
sense to me as something you request (PSK addendum) rather than, say,
forcing all PSKs and ASKs with 512-bit hashes to always be 512-bits,
which is excessive for secrecy.

Since we'd be treating the PSK addendum differently than the PSK, it
made sense to me (and keeps the ASK API simpler) to derive 2 separate
outputs which in combination form a collision-resistant hash, rather
than just deriving a single larger ASK.

At least that was the idea, let me know what you think.


> If we want the behavior of "invoke" to be independent of the hash function,
> we could rename "invoke_chain" to "read_chain" and model it using a "streaming API",
> where an arbitrary number of bytes can be read out (which becomes HKDF calls underneath).

I don't think "create_chains" or "invoke_chains" are great names in
any case, maybe you can think of something better?

But I tentatively think that the ability to derive multiple "chains"
and "ASKs" should be sufficient for the user to get as much key
material as they need (or you can derive an ASK and use it with your
own PRF if you need large amounts of padding or something).


> Is there a reason why we can not derive the ask_master only from the chaining key?

As you point out, users might want to call create_chains after ck has
been deleted at the end of the handshake.  (And ck should be deleted
for forward-secrecy, in particular if you use the Rekey mechanism, and
want to ensure you're not holding onto an old ck that can re-derive
old transport keys.)

However, you point out a list of trade-offs that result from this decision:


> Which would simplify the design considerably, by:
>
> - Avoid having to modify MixKey + MixKeyAndHash
> - Omitting the EnableASK method from the API (since there is no overhead unless create_chains is called).
> - Save HKDF calls when ASK is only used after a single MixKey/MixKeyAndHash call.
> - Allow us to remove the enable_ask flag and ask_master variable from the state
> - (optional) Allow us to enable the extraction of material prior to any
>   MixKey/MixKeyAndHash call, for use in obfuscation.
>
> One option might be to define create_chains as follows:
>
> """
> create_chains(labels):
>     ask_master = HKDF(ck, zerolen, info="ask", 1)
>     ask_chains = {}
>     for label in labels:
>         ask_chains[label] = HKDF(ask_master, h || label, 1)
>     erase ask_master
> """

Probably you could simplify even further?
"""
create_chains(labels):
    for label in labels:
        ask_chains[label] = HKDF(ask_master, h || label, info="ask", 1)
"""

(A small nuance is that we can delete ask_master after using it once,
but we can't do that with ck, so with this proposal we'd have to track
another boolean to prevent multiple create_chains calls on the same
ck.)


> Since the chaining key is deleted after Split(),
> users would have to call create_chains before Split(),
> but would still be able to invoke chains post Split().

Yeah, that's the trade-off.   I'm not sure preserving this ability is
worth all the costs you mention, though.

This might be too complicated:  but I wonder if we could get the best
of both worlds by deriving an ASK with label="posthandshake_ask"
during Split(), and using that in place of ck if the user calls
create_labels() post-handshake?

Trevor


More information about the Noise mailing list