[noise] Stateful Hash Object Proposal
Trevor Perrin
trevp at trevp.net
Fri Nov 16 02:12:28 PST 2018
My initial post on this was messy, here's a cleaned up proposal:
API
-------
StatefulHashObject {
Absorb(bytes)
Squeeze(len) -> bytes
Ratchet()
Clone() -> StatefulHashObject
// Below functions are optional.
// If supported, Encrypt() must have same properties as:
// key = Squeeze'(32);
// ciphertext = Authenticated-Encryption(key, plaintext)
// Absorb(ciphertext)
// return ciphertext
//
// where Squeeze' is a domain-separated version of Squeeze()
// and ciphertext is 16 bytes longer than plaintext
Encrypt(bytes) -> bytes
Decrypt(bytes) -> bytes
}
Example SHOs
-------------
>From traditional hashes (SHA256, BLAKE2, etc):
Absorb = incrementally hash
Squeeze = output the hash value
Ratchet = absorb zeros up to the next internal block boundary, so
that the internal buffer is cleared and the internal chaining variable
is updated
STROBE:
Absorb = AD
Squeeze = PRF(len)
Ratchet = RATCHET
Encrypt = ENC, PRF(16) # for tag
Decrypt = DEC, PRF(16) # for tag
SymmetricState
---------------
The Noise SymmetricState now contains a SHO. If the SHO doesn't
support Encrypt/Decrypt, the SymmetricState also contains a
CipherState with k and n variables (as before).
The functions are mostly the same, except GetSymmetricKey() and
GetAdditionalSymmetricKey() to calculate k and "Additional Symmetric
Keys" on demand:
MixKey(key):
SHO.Absorb(key)
MixHash(data):
SHO.Absorb(data)
MixKeyAndHash(data):
SHO.Absorb(data)
GetHandshakeHash():
clone = SHO.Clone()
clone.Absorb("h")
return clone.Squeeze(HASHLEN)
EncryptAndHash(plaintext):
if has_cipherstate:
// same as before
else:
return SHO.Encrypt(plaintext)
DecryptAndHash(ciphertext):
if has_cipherstate:
// same as before
else:
return SHO.Decrypt(ciphertext)
Split():
clone_i = SHO.Clone()
clone_i.Absorb("i")
key_i = clone_i.Squeeze(32)
clone_r = SHO.Clone()
clone_r.Absorb("r")
key_r = clone_r.Squeeze(32)
return (key_i, key_r)
GetSymmetricKey():
if has_cipherstate:
return self.k
else:
clone = Clone()
clone.Absorb("k")
return clone.Squeeze(32)
GetAdditionalSymmetricKey(label):
if has_cipherstate:
// todo, see [1]
else:
// varint length is byte-reversed so it's parseable from the end:
clone = Clone()
clone.Absorb(label || reversed_len(label) || "a")
return clone.Squeeze(32)
Processing rules
-----------------
Handshake patterns are compiled into SymmetricState operations mostly
the same as before, except:
* A varint length field is absorbed before the handshake payload and
the prologue. Any variable-length fields added in future will also
need a varint length absorbed before them.
* In interactive patterns, Ratchet() is called after processing the
handshake payload in each handshake message except the last one. This
ensures any internal buffer in the SHO can be "flushed" into the
internal chaining variable via a one-way function, so a compromise
can't recover the buffer's old contents (assuming enough entropy).
Naming
-------
The "SHO" variants of a hash function just add the "SHO/" prefix to the name:
SHO/SHA256
SHO/SHA512
SHO/BLAKE2s
SHO/BLAKE2b
SHO/SHAKE128
SHO/SHAKE256
etc
But other SHOs could be named differently, e.g. "STROBE/1.0.2".
Example
---------
The XX pattern derives 4 keys for handshake payload encryption, then
creates (via Split) two keys for traffic encryption. The keys for
Noise_XX_25519_AESGCM_SHO/SHA256 would be calculated as follows:
XX:
-> e
<- e, ee, [1] s, es, [2] [payload]
-> [3] s, se, [4] [payload]
[5,6] = split()
transcript = "Noise_XX_25519_AESGCM_SHO/SHA256" || len(prologue) ||
prologue || e)
transcript += zeros // pad to block boundary, for Ratchet
transcript += e || ee
key1 = SHA256(transcript || "k")
transcript += s || es
key2 = SHA256(transcript || "k")
transcript += len(payload) || payload
transcript += zeros // pad to block boundary, for Ratchet
key3 = SHA256(transcript || "k")
transcript4 += s || se
key4 = SHA256(transcript || "k")
transcript += len(payload) || payload
key_i = SHA256(transcript || "i")
key_r = SHA256(transcript || "r")
Trevor
[1] https://moderncrypto.org/mail-archive/noise/2018/001853.html
More information about the Noise
mailing list