mirror of https://github.com/cbeuw/Cloak
Add comments
This commit is contained in:
parent
5af55d62d8
commit
977f51ce48
|
|
@ -45,10 +45,38 @@ type Obfuscator struct {
|
||||||
// is in the byte slice used as buffer (2nd argument). payloadOffsetInBuf specifies
|
// is in the byte slice used as buffer (2nd argument). payloadOffsetInBuf specifies
|
||||||
// the index at which data belonging to *Frame.Payload starts in the buffer.
|
// the index at which data belonging to *Frame.Payload starts in the buffer.
|
||||||
func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Obfser {
|
func MakeObfs(salsaKey [32]byte, payloadCipher cipher.AEAD) Obfser {
|
||||||
|
// The method here is to use the first payloadCipher.NonceSize() bytes of the serialised frame header
|
||||||
|
// as iv/nonce for the AEAD cipher to encrypt the frame payload. Then we use
|
||||||
|
// the authentication tag produced appended to the end of the ciphertext (of size payloadCipher.Overhead())
|
||||||
|
// as nonce for Salsa20 to encrypt the frame header. Both with SessionKey as keys.
|
||||||
|
//
|
||||||
|
// Several cryptographic guarantees we have made here: that payloadCipher, as an AEAD, is given a unique
|
||||||
|
// iv/nonce each time, relative to its key; that the frame header encryptor Salsa20 is given a unique
|
||||||
|
// nonce each time, relative to its key; and that the authenticity of frame header is checked.
|
||||||
|
//
|
||||||
|
// The payloadCipher is given a unique iv/nonce each time because it is derived from the frame header, which
|
||||||
|
// contains the monotonically increasing stream id (uint32) and frame sequence (uint64). There will be a nonce
|
||||||
|
// reuse after 2^64-1 frames sent (sent, not received because frames going different ways are sequenced
|
||||||
|
// independently) by a stream, or after 2^32-1 streams created in a single session. We consider these number
|
||||||
|
// to be large enough that they may never happen in reasonable time frames. Of course, different sessions
|
||||||
|
// will produce the same combination of stream id and frame sequence, but they will have different session keys.
|
||||||
|
//
|
||||||
|
// Salsa20 is assumed to be given a unique nonce each time because we assume the tags produced by payloadCipher
|
||||||
|
// AEAD is unique each time, as payloadCipher itself is given a unique iv/nonce each time due to points made above.
|
||||||
|
// This is relatively a weak guarantee as we are assuming AEADs to produce different tags given different iv/nonces.
|
||||||
|
// This is almost certainly true but I cannot find a source that outright states this.
|
||||||
|
//
|
||||||
|
// Because the frame header, before it being encrypted, is fed into the AEAD, it is also authenticated.
|
||||||
|
// (rfc5116 s.2.1 "The nonce is authenticated internally to the algorithm").
|
||||||
|
//
|
||||||
|
// In case the user chooses to not encrypt the frame payload, payloadCipher will be nil. In this scenario,
|
||||||
|
// we pad the frame payload with random bytes until it reaches Salsa20's nonce size (8 bytes). Then we simply
|
||||||
|
// encrypt the frame header with the last 8 bytes of frame payload as nonce.
|
||||||
|
// If the payload provided by the user is greater than 8 bytes, then we use entirely the user input as nonce.
|
||||||
|
// We can't ensure its uniqueness ourselves, which is why plaintext mode must only be used when the user input
|
||||||
|
// is already random-like. For Cloak it would normally mean that the user is using a proxy protocol that sends
|
||||||
|
// encrypted data.
|
||||||
obfs := func(f *Frame, buf []byte, payloadOffsetInBuf int) (int, error) {
|
obfs := func(f *Frame, buf []byte, payloadOffsetInBuf int) (int, error) {
|
||||||
// we need the encrypted data to be at least 8 bytes to be used as nonce for salsa20 stream header encryption
|
|
||||||
// this will be the case if the encryption method is an AEAD cipher, however for plain, it's well possible
|
|
||||||
// that the frame payload is smaller than 8 bytes, so we need to add on the difference
|
|
||||||
payloadLen := len(f.Payload)
|
payloadLen := len(f.Payload)
|
||||||
if payloadLen == 0 {
|
if payloadLen == 0 {
|
||||||
return 0, errors.New("payload cannot be empty")
|
return 0, errors.New("payload cannot be empty")
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,8 @@ type Stream struct {
|
||||||
// been read by the consumer through Read or WriteTo
|
// been read by the consumer through Read or WriteTo
|
||||||
recvBuf recvBuffer
|
recvBuf recvBuffer
|
||||||
|
|
||||||
nextSendSeq uint64
|
|
||||||
|
|
||||||
writingM sync.Mutex
|
writingM sync.Mutex
|
||||||
|
nextSendSeq uint64
|
||||||
|
|
||||||
// atomic
|
// atomic
|
||||||
closed uint32
|
closed uint32
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,8 @@ const (
|
||||||
UNIFORM_SPREAD
|
UNIFORM_SPREAD
|
||||||
)
|
)
|
||||||
|
|
||||||
// switchboard is responsible for managing TCP connections between client and server.
|
// switchboard represents the connection pool. It is responsible for managing
|
||||||
|
// transport-layer connections between client and server.
|
||||||
// It has several purposes: constantly receiving incoming data from all connections
|
// It has several purposes: constantly receiving incoming data from all connections
|
||||||
// and pass them to Session.recvDataFromRemote(); accepting data through
|
// and pass them to Session.recvDataFromRemote(); accepting data through
|
||||||
// switchboard.send(), in which it selects a connection according to its
|
// switchboard.send(), in which it selects a connection according to its
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue