[noise] Symmetric-crypto overhaul and stateful hashing

Paul Rösler paul.roesler at rub.de
Tue Nov 13 10:58:02 PST 2018


Hi again,

sorry for formulating my comments not too kind.

On 13.11.18 19:06, Trevor Perrin wrote:
>> I like the idea of generalizing and combining compatible building
>> blocks into a more abstract primitive. However, mixing more independent
>> functionality into a primitive makes it complicated to define and
>> understand security. As a secure key computation can always be composed
>> with a secure cipher, I do not see the reason for putting both into one
>> primitive (at least from a theoretical point of view).
> 
> Good question.  I think the main benefit is that for an object (like
> STROBE) based on permutation-crypto (like Keccak), doing Encrypt()
> would simultaneously absorb the input into the hash state.
> 
> So we would *not* have to do a separate Absorb(ciphertext) after
> encrypting, since this would already be taken care of, giving some
> simplicity and speed benefits.
I see that this saves one line of code and maybe (due to the allowed
interleave of operations Enc and Absorb within STROBE-like
constructions) a bit more efficiency.

> I agree that it makes the analysis a lot more complicated, though.
It is maybe not only a bit more complicated for an analysis, but
potentially also for developers and users less comprehensible regarding
the primitive's idea: to me it is not really clear what the nature of a
stateful hash with encryption is. The feature described above (absorbing
during encryption) is from my point of view not really obvious to a user
(i.e., one may not know that an encryption immediately triggers absorbing).

>>>  * Decouple "absorbing" from "ratcheting" so we can absorb inputs more
>>> efficiently and only "ratchet" when needed for forward-secrecy.
>> Sounds legit, however I am not sure whether this is understood well by
>> all developers. So in order to prevent squeezing without ratcheting
>> (i.e., one squeezes without using the new absorbed entropy), the API
>> should take care of this (e.g., by always ratcheting if something was
>> absorbed recently, but ratcheting was not actively invoked).
> 
> I wasn't very clear there either, I was thinking of "ratcheting" as an
> operation that ensures all previous inputs are mixed together, so that
> if the state has enough entropy it can't be "inverted" to recover any
> old inputs.  So this would be done for forward-secrecy, whenever we've
> used our keys for encryption/decryption and then have to wait a period
> of time in which we might be compromised.
> 
> [...]
> 
> I agree that deciding when to do this ratcheting is subtle, and
> something we'd have to think about carefully.  Currently we just do it
> on every HKDF, which is both too much and in some cases too little,
> e.g. in XX it would be nice to ratchet after the responder's first
> message, but Noise currently reuses the same k to process the
> initiator's response.
Thanks for clarifying, but I just wanted to comment that maybe after
Absorb(..); and before ..<-Squeeze(..); automatically a Ratchet();
should be triggered within Squeeze in case the developer forgets to
explicitly invoke it.

>>>  * Noise will never squeeze more than HASHLEN bytes of output, for
>>> compatibility with traditional hashes.
>> As you are aiming for a generic primitive, why should the squeeze
>> operation not require its (hash-based) implementation to generate as
>> much output as requested (e.g., by iteratively ratcheting and squeezing)?
> 
> I'd like the "traditional hash" implementation to just compile down to
> a single hash call, ie a sequence of Absorbs followed by a Squeeze
> should just turn into:
> 
> k = SHA256(absorb1 || absorb2 || ...)
> 
> If we required "extensibible", XOF-style output we'd have to use
> HKDF-Expand or something here.  I think it's simpler for Noise to
> produce different outputs by just appending different labels to every
> output:
> 
> k = SHA256(absorb1 || absorb2 || ... || "k")
> h = SHA256(absorb1 || absorb2 || ... || "h")
Okay, I understand, but you could also do:
k||h||x||y <- Squeeze(4*HASHLEN)

Squeeze(n):
  output = empty
  for(i=0;i =< n/HASHLEN;i++):
    output <- output||SHA256(absorb1 || absorb2 || ... || i)
  return output

Cheers,
Paul

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


More information about the Noise mailing list