control/noise: review fixups
Signed-off-by: David Anderson <danderson@tailscale.com>
This commit is contained in:
committed by
Dave Anderson
parent
0b392dbaf7
commit
cf90392174
+72
-40
@@ -6,32 +6,68 @@ package noise
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const (
|
||||
msgTypeInitiation = 1
|
||||
msgTypeResponse = 2
|
||||
msgTypeError = 3
|
||||
msgTypeRecord = 4
|
||||
)
|
||||
|
||||
// headerLen is the size of the cleartext message header that gets
|
||||
// prepended to Noise messages.
|
||||
// The transport protocol is mostly Noise messages encapsulated in a
|
||||
// small header describing the payload's type and length. The one
|
||||
// place we deviate from pure Noise+header is that we also support
|
||||
// sending an unauthenticated plaintext error as payload, to provide
|
||||
// an explanation for a connection error that happens before the
|
||||
// handshake completes.
|
||||
//
|
||||
// All frames in our protocol have a 5-byte header:
|
||||
//
|
||||
// +------+------+------+------+------+
|
||||
// | version | type | length |
|
||||
// +------+------+------+------+------+
|
||||
//
|
||||
// 2b: protocol version
|
||||
// 1b: message type
|
||||
// 2b: payload length (not including this header)
|
||||
// 2b: payload length (not including the header)
|
||||
//
|
||||
// Multibyte values are all big-endian on the wire, as is traditional
|
||||
// for network protocols.
|
||||
//
|
||||
// The protocol version is 2 bytes in order to encourage frequent
|
||||
// revving of the protocol as needed, without fear of running out of
|
||||
// version numbers. At minimum, the version number must change
|
||||
// whenever any particulars of the Noise handshake change
|
||||
// (e.g. switching from Noise IK to Noise IKpsk1 or Noise XX), and
|
||||
// when security-critical aspects of the "uppper" protocol within the
|
||||
// Noise frames change (e.g. how further authentication data is bound
|
||||
// to the underlying Noise session).
|
||||
|
||||
// headerLen is the size of the header that gets prepended to Noise
|
||||
// messages.
|
||||
const headerLen = 5
|
||||
|
||||
func setHeader(bs []byte, version int, msgType byte, length int) {
|
||||
const (
|
||||
// msgTypeInitiation frames carry a Noise IK handshake initiation message.
|
||||
msgTypeInitiation = 1
|
||||
// msgTypeResponse frames carry a Noise IK handshake response message.
|
||||
msgTypeResponse = 2
|
||||
// msgTypeError frames carry an unauthenticated human-readable
|
||||
// error message.
|
||||
//
|
||||
// Errors reported in this message type must be treated as public
|
||||
// hints only. They are not encrypted or authenticated, and so can
|
||||
// be seen and tampered with on the wire.
|
||||
msgTypeError = 3
|
||||
// msgTypeRecord frames carry a Noise transport message (i.e. "user data").
|
||||
msgTypeRecord = 4
|
||||
)
|
||||
|
||||
func setHeader(bs []byte, version uint16, msgType byte, length int) {
|
||||
binary.LittleEndian.PutUint16(bs[:2], uint16(version))
|
||||
bs[2] = msgType
|
||||
binary.LittleEndian.PutUint16(bs[3:5], uint16(length))
|
||||
}
|
||||
func hdrVersion(bs []byte) int { return int(binary.LittleEndian.Uint16(bs[:2])) }
|
||||
func hdrType(bs []byte) byte { return bs[2] }
|
||||
func hdrLen(bs []byte) int { return int(binary.LittleEndian.Uint16(bs[3:5])) }
|
||||
func hdrVersion(bs []byte) uint16 { return binary.LittleEndian.Uint16(bs[:2]) }
|
||||
func hdrType(bs []byte) byte { return bs[2] }
|
||||
func hdrLen(bs []byte) int { return int(binary.LittleEndian.Uint16(bs[3:5])) }
|
||||
|
||||
// initiationMessage is the Noise protocol message sent from a client
|
||||
// machine to a control server.
|
||||
// machine to a control server. Aside from the message header, the
|
||||
// values are as specified in the Noise specification for the IK
|
||||
// handshake pattern.
|
||||
//
|
||||
// 5b: header (see headerLen for fields)
|
||||
// 32b: client ephemeral public key (cleartext)
|
||||
@@ -41,47 +77,43 @@ type initiationMessage [101]byte
|
||||
|
||||
func mkInitiationMessage() initiationMessage {
|
||||
var ret initiationMessage
|
||||
binary.LittleEndian.PutUint16(ret[:2], protocolVersion)
|
||||
ret[2] = msgTypeInitiation
|
||||
binary.LittleEndian.PutUint16(ret[3:5], 96)
|
||||
setHeader(ret[:], protocolVersion, msgTypeInitiation, len(ret.Payload()))
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *initiationMessage) Header() []byte { return m[:5] }
|
||||
func (m *initiationMessage) Payload() []byte { return m[5:] }
|
||||
func (m *initiationMessage) Header() []byte { return m[:headerLen] }
|
||||
func (m *initiationMessage) Payload() []byte { return m[headerLen:] }
|
||||
|
||||
func (m *initiationMessage) Version() int { return hdrVersion(m.Header()) }
|
||||
func (m *initiationMessage) Type() byte { return hdrType(m.Header()) }
|
||||
func (m *initiationMessage) Length() int { return hdrLen(m.Header()) }
|
||||
func (m *initiationMessage) Version() uint16 { return hdrVersion(m.Header()) }
|
||||
func (m *initiationMessage) Type() byte { return hdrType(m.Header()) }
|
||||
func (m *initiationMessage) Length() int { return hdrLen(m.Header()) }
|
||||
|
||||
func (m *initiationMessage) EphemeralPub() []byte { return m[5:37] }
|
||||
func (m *initiationMessage) MachinePub() []byte { return m[37:85] }
|
||||
func (m *initiationMessage) Tag() []byte { return m[85:] }
|
||||
func (m *initiationMessage) EphemeralPub() []byte { return m[headerLen : headerLen+32] }
|
||||
func (m *initiationMessage) MachinePub() []byte { return m[headerLen+32 : headerLen+32+48] }
|
||||
func (m *initiationMessage) Tag() []byte { return m[headerLen+32+48:] }
|
||||
|
||||
// responseMessage is the Noise protocol message sent from a control
|
||||
// server to a client machine.
|
||||
// server to a client machine. Aside from the message header, the
|
||||
// values are as specified in the Noise specification for the IK
|
||||
// handshake pattern.
|
||||
//
|
||||
// 2b: little-endian protocol version
|
||||
// 1b: message type
|
||||
// 2b: little-endian size of message (not including this header)
|
||||
// 5b: header (see headerLen for fields)
|
||||
// 32b: control ephemeral public key (cleartext)
|
||||
// 16b: message tag (authenticates the whole message)
|
||||
type responseMessage [53]byte
|
||||
|
||||
func mkResponseMessage() responseMessage {
|
||||
var ret responseMessage
|
||||
binary.LittleEndian.PutUint16(ret[:2], protocolVersion)
|
||||
ret[2] = msgTypeResponse
|
||||
binary.LittleEndian.PutUint16(ret[3:5], 48)
|
||||
setHeader(ret[:], protocolVersion, msgTypeResponse, len(ret.Payload()))
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m *responseMessage) Header() []byte { return m[:5] }
|
||||
func (m *responseMessage) Payload() []byte { return m[5:] }
|
||||
func (m *responseMessage) Header() []byte { return m[:headerLen] }
|
||||
func (m *responseMessage) Payload() []byte { return m[headerLen:] }
|
||||
|
||||
func (m *responseMessage) Version() int { return hdrVersion(m.Header()) }
|
||||
func (m *responseMessage) Type() byte { return hdrType(m.Header()) }
|
||||
func (m *responseMessage) Length() int { return hdrLen(m.Header()) }
|
||||
func (m *responseMessage) Version() uint16 { return hdrVersion(m.Header()) }
|
||||
func (m *responseMessage) Type() byte { return hdrType(m.Header()) }
|
||||
func (m *responseMessage) Length() int { return hdrLen(m.Header()) }
|
||||
|
||||
func (m *responseMessage) EphemeralPub() []byte { return m[5:37] }
|
||||
func (m *responseMessage) Tag() []byte { return m[37:] }
|
||||
func (m *responseMessage) EphemeralPub() []byte { return m[headerLen : headerLen+32] }
|
||||
func (m *responseMessage) Tag() []byte { return m[headerLen+32:] }
|
||||
|
||||
Reference in New Issue
Block a user