brontide/noise: add Flush method
This commit adds a Flush method to the brontide.Machine, which can write out a buffered message to an io.Writer. This is a preliminary change which will allow the encryption of the plaintext to be done in a distinct method from actually writing the bytes to the wire.
This commit is contained in:
parent
6e3cf06bd4
commit
ed8fe4bc82
@ -367,6 +367,16 @@ type Machine struct {
|
|||||||
// next ciphertext header from the wire. The header is a 2 byte length
|
// next ciphertext header from the wire. The header is a 2 byte length
|
||||||
// (of the next ciphertext), followed by a 16 byte MAC.
|
// (of the next ciphertext), followed by a 16 byte MAC.
|
||||||
nextCipherHeader [lengthHeaderSize + macSize]byte
|
nextCipherHeader [lengthHeaderSize + macSize]byte
|
||||||
|
|
||||||
|
// nextHeaderSend holds a reference to the remaining header bytes to
|
||||||
|
// write out for a pending message. This allows us to tolerate timeout
|
||||||
|
// errors that cause partial writes.
|
||||||
|
nextHeaderSend []byte
|
||||||
|
|
||||||
|
// nextHeaderBody holds a reference to the remaining body bytes to write
|
||||||
|
// out for a pending message. This allows us to tolerate timeout errors
|
||||||
|
// that cause partial writes.
|
||||||
|
nextBodySend []byte
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewBrontideMachine creates a new instance of the brontide state-machine. If
|
// NewBrontideMachine creates a new instance of the brontide state-machine. If
|
||||||
@ -715,6 +725,81 @@ func (b *Machine) WriteMessage(w io.Writer, p []byte) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Flush attempts to write a message buffered using WriteMessage to the provided
|
||||||
|
// io.Writer. If no buffered message exists, this will result in a NOP.
|
||||||
|
// Otherwise, it will continue to write the remaining bytes, picking up where
|
||||||
|
// the byte stream left off in the event of a partial write. The number of bytes
|
||||||
|
// returned reflects the number of plaintext bytes in the payload, and does not
|
||||||
|
// account for the overhead of the header or MACs.
|
||||||
|
//
|
||||||
|
// NOTE: It is safe to call this method again iff a timeout error is returned.
|
||||||
|
func (b *Machine) Flush(w io.Writer) (int, error) {
|
||||||
|
// First, write out the pending header bytes, if any exist. Any header
|
||||||
|
// bytes written will not count towards the total amount flushed.
|
||||||
|
if len(b.nextHeaderSend) > 0 {
|
||||||
|
// Write any remaining header bytes and shift the slice to point
|
||||||
|
// to the next segment of unwritten bytes. If an error is
|
||||||
|
// encountered, we can continue to write the header from where
|
||||||
|
// we left off on a subsequent call to Flush.
|
||||||
|
n, err := w.Write(b.nextHeaderSend)
|
||||||
|
b.nextHeaderSend = b.nextHeaderSend[n:]
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Next, write the pending body bytes, if any exist. Only the number of
|
||||||
|
// bytes written that correspond to the ciphertext will be included in
|
||||||
|
// the total bytes written, bytes written as part of the MAC will not be
|
||||||
|
// counted.
|
||||||
|
var nn int
|
||||||
|
if len(b.nextBodySend) > 0 {
|
||||||
|
// Write out all bytes excluding the mac and shift the body
|
||||||
|
// slice depending on the number of actual bytes written.
|
||||||
|
n, err := w.Write(b.nextBodySend)
|
||||||
|
b.nextBodySend = b.nextBodySend[n:]
|
||||||
|
|
||||||
|
// If we partially or fully wrote any of the body's MAC, we'll
|
||||||
|
// subtract that contribution from the total amount flushed to
|
||||||
|
// preserve the abstraction of returning the number of plaintext
|
||||||
|
// bytes written by the connection.
|
||||||
|
//
|
||||||
|
// There are three possible scenarios we must handle to ensure
|
||||||
|
// the returned value is correct. In the first case, the write
|
||||||
|
// straddles both payload and MAC bytes, and we must subtract
|
||||||
|
// the number of MAC bytes written from n. In the second, only
|
||||||
|
// payload bytes are written, thus we can return n unmodified.
|
||||||
|
// The final scenario pertains to the case where only MAC bytes
|
||||||
|
// are written, none of which count towards the total.
|
||||||
|
//
|
||||||
|
// |-----------Payload------------|----MAC----|
|
||||||
|
// Straddle: S---------------------------------E--------0
|
||||||
|
// Payload-only: S------------------------E-----------------0
|
||||||
|
// MAC-only: S-------E-0
|
||||||
|
start, end := n+len(b.nextBodySend), len(b.nextBodySend)
|
||||||
|
switch {
|
||||||
|
|
||||||
|
// Straddles payload and MAC bytes, subtract number of MAC bytes
|
||||||
|
// written from the actual number written.
|
||||||
|
case start > macSize && end <= macSize:
|
||||||
|
nn = n - (macSize - end)
|
||||||
|
|
||||||
|
// Only payload bytes are written, return n directly.
|
||||||
|
case start > macSize && end > macSize:
|
||||||
|
nn = n
|
||||||
|
|
||||||
|
// Only MAC bytes are written, return 0 bytes written.
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nn, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nn, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReadMessage attempts to read the next message from the passed io.Reader. In
|
// ReadMessage attempts to read the next message from the passed io.Reader. In
|
||||||
// the case of an authentication error, a non-nil error is returned.
|
// the case of an authentication error, a non-nil error is returned.
|
||||||
func (b *Machine) ReadMessage(r io.Reader) ([]byte, error) {
|
func (b *Machine) ReadMessage(r io.Reader) ([]byte, error) {
|
||||||
|
Loading…
Reference in New Issue
Block a user