[noise] Resumption PSKs

str4d str4d at i2pmail.org
Fri Jun 8 16:27:48 PDT 2018


On 06/08/2018 03:54 AM, Christopher Wood wrote:
> On Thu, Jun 7, 2018 at 12:02 AM Trevor Perrin <trevp at trevp.net> wrote:
>>
>> On Tue, Jun 5, 2018 at 2:38 PM, Christopher Wood
>> <christopherwood07 at gmail.com> wrote:
>>> I'm not advocating
>>> for an API that allows clients to pump out keying material
>>> indefinitely from the same root. I was
>>> just hoping to avoid overloading an already well-defined and clearly
>>> specified API.
>>
>> Makes sense as a goal, but if you don't want to change Split()'s
>> interface, I think the options are:
>>
>> (A) A function that can be called multiple times, after Split(), to
>> create new key chains or additional keys.  This probably requires
>> storing a single key and deriving from it repeatedly, thus defeating
>> the key-independence / forward-secrecy goal.
>>
>> (B) A function that can be called once after Split() to create a new
>> set of key chains, by deriving them from an extra key that is then
>> destroyed.  This wouldn't change the underlying crypto, but it would
>> mean that uses of Split() that don't require this extra key would have
>> to derive and store it, in case this later function was called.
>>
>> Since changing Split() to take additional optional arguments is just a
>> notation / API thing, it seems better than these alternatives which
>> would have actual downsides in security properties or efficiency.
> 
> In both examples, you note that Split() is always invoked. I
> envisioned the new function being called in lieu of Split(), since
> Split() does not modify the SymmetricState (right?). From the spec, it
> only seems to be called when message patterns are finished.
> Applications call this as needed during the handshake. At the end of
> the handshake, they may call Split() to get CipherState objects for
> encrypting/decrypting transport data, or, as suggested, get derived
> keys to use for other purposes.

This doesn't cover the case where you want to do both (such as in my use
case where I want both transport keys and parallel key material).
Applications would need to be able to call both functions (and the API
will likely need to support calling them in either order).

> 
> For clarity, below is what I think you proposed. Please correct me if
> it's wrong!
> 
> ~~~
> Split(labels [][]byte) {
>   Sets temp_k1, temp_k2, temp_k3 = HKDF(ck, zerolen, 3)
>   If HASHLEN is 64, then truncates temp_k1, temp_k2, temp_k2 to 32 bytes
>   Creates two new CipherState objects c1 and c2
>   Calls c1.InitializeKey(temp_k1) and c2.InitializeKey(temp_k2)
>   Create list `keychains` that stores label keys
>   For each label_i = labels[i], create ck_i = HMAC(temp_k3, h ||
> label_i), and set keychains[i] = ck_i
>   Returns list (c1, c2, keychains)
> }
> ~~~

This almost matches my draft implementation, except that temp_k3 is not
truncated before it is used as an input to HMAC() (I assume that's what
you meant by "truncates temp_k1, temp_k2, temp_k2 to 32 bytes"), and h
isn't used as a prefix yet.

> 
> Here's what I was proposing:
> 
> ~~~
> Split() {
>    // Same as today
> }
> 
> Foo(labels [][]byte) {
>   Sets temp_k1 = HKDF(ck, zerolen, 1)
>   Create list `keychains` that stores label keys
>   For each label_i = labels[i], create ck_i = HMAC(temp_k1, h ||
> label_i), and set keychains[i] = ck_i
>   Returns keychains
> }
> ~~~

I don't think this is compatible with wanting to use both Split() and
Foo(labels). temp_k1 will be the same in both functions, meaning that if
you call "Split(); Foo();" then the key chains are all derived from c1's
key. Granted, if CIPHERKEYLEN < 64 then c1 does not store the entire
key, but they are still not really independent, and that leaves a
footgun in place triggerable by the user's choice of cipher.

> 
> At the end of the day, this boils down to cleanliness of the API, I
> think, so this distinction is likely not critical. I do think it's
> worth considering, though.

Here are the various API options as I see them:

1) Split() takes optional argument (labels), and returns as many key
chains as it is given labels.

2) A new function Foo(labels) is added, that requires calling Split() first.

- This can be split (no pun intended) into options (A) and (B) that
Trevor proposed.

3) A new function Foo(labels) is added, that doesn't require calling
Split().

- Split() and Foo() need to be completely independent (see above). Being
distinct functions, we'd either need to use domain separation (though
ensuring that Foo() is appropriately domain-separated without altering
Split() could be tricky), or derive three keys in Foo() and discard the
first two.

- Implementation-wise, this would probably mean that a library's
HandshakeState is instantiated with a flag per function, to enable or
disable calling it (which could be an Option<Arguments> for Foo()).

4) A new function SplitAk(labels) is added, which is identical to
Split() from 1).

- Requires duplicating the internal logic of Split() :(

- Implementations could just toggle between the two with a single flag
(or an Option<labels>).

- Possibly a middle-ground between reducing configuration complexity,
and avoiding changes to Split()'s API.


If this is likely to be the only addition to Split() then I'm happy with
1). Implementation-wise, it was pretty straightforward, and in the case
where no labels are provided, it would return an empty array, which I
don't think is at all onerous. It isn't necessarily a good template for
how to make further undefined changes to Split(), however - though with
key chains enabling arbitrary amounts of parallel key material to be
derived, it's not clear to me if there's _anything_ more that might want
to be added.

I agree with Trevor's thoughts on 2).

3) is the most extensible, but future extensions would similarly need to
ensure they are completely independent of both Split() and Foo().
Depending on how Foo() is made independent, this could start becoming
unwieldy.

4) seems like the most conservative approach from an API standpoint. I'm
not particularly fond of it, even though I originally proposed it (:

Cheers,
str4d

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: OpenPGP digital signature
URL: <http://moderncrypto.org/mail-archive/noise/attachments/20180608/e6922025/attachment.sig>


More information about the Noise mailing list