[noise] Additional Symmetric Keys (ASK)

Trevor Perrin trevp at trevp.net
Wed Jun 20 21:37:02 PDT 2018


Hi all,

The thread on "Resumption PSKs" was driven forward by str4d and
Christopher Wood and used to discuss the general case of deriving
additional symmetric keys during or after a Noise handshake, for
various uses.

I think the discussion has settled on a proposal for an "Additional
Symmetric Key" (or "ASK") mechanism, which some people would like to
implement soon.

Let me summarize where I think the discussion is, so others can catch
up and comment.


Use cases
---
The ASK mechanism can be used by the application to derive ASKs after
the handshake or even during it.  It could be used for several cases:

 * Resumption PSKs - Deriving a "resumption PSK" from an initial Noise
session.  The resumption PSK can be used to perform a PSK Noise
handshake later.  The mechanism could also derive associated data,
such as labels to identify PSKs, or the ideas in [1].

 * Deriving keys used to generate random padding or other
length-hiding / traffic-hiding countermeasures. str4d has discussed
this in the context of I2P.

 * Generate application-layer keys in case the Noise handshake is
being used as an authentication / key-generation component within some
other protocol, which I think Chris has mentioned.  (While the
transport keys could sometimes be reused here, in some Noise protocols
the transport keys must be used to send an initial Noise message to
archive the strongest security properties, so in this case it would be
better to generate fresh ASKs for other uses).


Design goals
---
One security goal is that after a Noise library returns some ASKs, the
keys still held by the library should be independent from those ASKs
(i.e. can't derive each other).

Another security goal is that the output keys should be capable of
serving as collision-resistant hashes of the session transcript, each
if the session doesn't involve any secret keys.

The mechanism should be extensible to other sorts of ASKs (i.e. not
hard-coded to the use cases above).

The mechanism should be "zero cost" if not used, so would be an
optional feature that libraries could offer and applications could
take advantage of.   (This means it will probably be defined in an
extension document, not the main Noise spec).


API
----
The user will interact with the mechanism through an API something
like this (taken from [2]):

"""
EnableASK():
  - sets a boolean that causes ASK master keys to be derived, both during
the handshake and at the end of it.

InitializeASK(labels):
  - Assuming ASK was enabled, and there is a non-empty ASK master key,
derives a set of ASK chain keys from the ASK master key and the input
labels, then deletes the ASK master key.  This could be called multiple
times, including after the handshake and during it, and replaces any
previous ASK chains when called.

GetASK(label)
 - Assuming ASK is enabled and initialized, returns the next key from the
appropriate chain, and advances the appropriate ASK chain key, deleting the
previous chain key.

So if you don't EnableASK(), you don't pay any costs for deriving ASK
master keys.  If you do enable it, they you can call InitializeASK/GetASK
either during a handshake or after it, to get ASKs with the same level of
security as the current encryption keys.
"""


Implementations
---
The implementation will use HKDF as follows (taken from st4d's emails,
e.g. [3]).

"""
# During handshake (in both MixKey and MixKeyAndHash)
if enable_ask:
    ask_master = HKDF(ck, ikm, info="ask")
ck, k = HKDF(ck, ikm)

# During Split
# Can technically be arranged as before, but may as well be consistent
if enable_ask:
    ask_master = HKDF(ck, zerolen, info="ask")
transport1, transport2 = HKDF(ck, zerolen)

create_chains(labels):
    if not (enable_ask and ask_master):
        return Err()
    ask_chains = {}
    for label in labels:
        ask_chains[label] = HKDF(ask_master, h || label)
    delete ask_master

# Suggestions for better name welcome!
invoke_chain(label):
    if not (enable_ask and ask_chains and ask_chains[label]):
        return Err()
    ask_ck = ask_chains[label]
    temp_k1, temp_k2 = HKDF(ask_ck, zerolen)
    ask_chains[label] = temp_k1
    return temp_k2[..32]
"""

Note that the output keys are a function of some "h" value.  For
post-handshake ASKs, this should be the final handshake hash.  For
pre-handshake ASKs, this should probably be the h value just prior to
processing the handshake message's payload.

Status
---
Chris was thinking of starting a spec for this, and I believe str4d
needs it finalized very soon for use in I2P, so rapid feedback is
welcome.

[1] https://moderncrypto.org/mail-archive/noise/2018/001636.html
[2] https://moderncrypto.org/mail-archive/noise/2018/001669.html
[3] https://moderncrypto.org/mail-archive/noise/2018/001669.html


Trevor


More information about the Noise mailing list