@ -32,7 +32,7 @@ const (
protocolName = "Noise_IK_25519_ChaChaPoly_BLAKE2s"
protocolName = "Noise_IK_25519_ChaChaPoly_BLAKE2s"
// protocolVersion is the version of the control protocol that
// protocolVersion is the version of the control protocol that
// Client will use when initiating a handshake.
// Client will use when initiating a handshake.
protocolVersion uint16 = 1
//protocolVersion uint16 = 1
// protocolVersionPrefix is the name portion of the protocol
// protocolVersionPrefix is the name portion of the protocol
// name+version string that gets mixed into the handshake as a
// name+version string that gets mixed into the handshake as a
// prologue.
// prologue.
@ -66,7 +66,7 @@ type HandshakeContinuation func(context.Context, net.Conn) (*Conn, error)
// protocol switching. By splitting the handshake into an initial
// protocol switching. By splitting the handshake into an initial
// message and a continuation, we can embed the handshake initiation
// message and a continuation, we can embed the handshake initiation
// into the HTTP protocol switching request and avoid a bit of delay.
// into the HTTP protocol switching request and avoid a bit of delay.
func ClientDeferred ( machineKey key . MachinePrivate , controlKey key . MachinePublic ) ( initialHandshake [ ] byte , continueHandshake HandshakeContinuation , err error ) {
func ClientDeferred ( machineKey key . MachinePrivate , controlKey key . MachinePublic , protocolVersion uint16 ) ( initialHandshake [ ] byte , continueHandshake HandshakeContinuation , err error ) {
var s symmetricState
var s symmetricState
s . Initialize ( )
s . Initialize ( )
@ -78,7 +78,7 @@ func ClientDeferred(machineKey key.MachinePrivate, controlKey key.MachinePublic)
s . MixHash ( controlKey . UntypedBytes ( ) )
s . MixHash ( controlKey . UntypedBytes ( ) )
// -> e, es, s, ss
// -> e, es, s, ss
init := mkInitiationMessage ( )
init := mkInitiationMessage ( protocolVersion )
machineEphemeral := key . NewMachine ( )
machineEphemeral := key . NewMachine ( )
machineEphemeralPub := machineEphemeral . Public ( )
machineEphemeralPub := machineEphemeral . Public ( )
copy ( init . EphemeralPub ( ) , machineEphemeralPub . UntypedBytes ( ) )
copy ( init . EphemeralPub ( ) , machineEphemeralPub . UntypedBytes ( ) )
@ -96,7 +96,7 @@ func ClientDeferred(machineKey key.MachinePrivate, controlKey key.MachinePublic)
s . EncryptAndHash ( cipher , init . Tag ( ) , nil ) // empty message payload
s . EncryptAndHash ( cipher , init . Tag ( ) , nil ) // empty message payload
cont := func ( ctx context . Context , conn net . Conn ) ( * Conn , error ) {
cont := func ( ctx context . Context , conn net . Conn ) ( * Conn , error ) {
return continueClientHandshake ( ctx , conn , & s , machineKey , machineEphemeral , controlKey )
return continueClientHandshake ( ctx , conn , & s , machineKey , machineEphemeral , controlKey , protocolVersion )
}
}
return init [ : ] , cont , nil
return init [ : ] , cont , nil
}
}
@ -107,8 +107,8 @@ func ClientDeferred(machineKey key.MachinePrivate, controlKey key.MachinePublic)
// This is a helper for when you don't need the fancy
// This is a helper for when you don't need the fancy
// continuation-style handshake, and just want to synchronously
// continuation-style handshake, and just want to synchronously
// upgrade a net.Conn to a secure transport.
// upgrade a net.Conn to a secure transport.
func Client ( ctx context . Context , conn net . Conn , machineKey key . MachinePrivate , controlKey key . MachinePublic ) ( * Conn , error ) {
func Client ( ctx context . Context , conn net . Conn , machineKey key . MachinePrivate , controlKey key . MachinePublic , protocolVersion uint16 ) ( * Conn , error ) {
init , cont , err := ClientDeferred ( machineKey , controlKey )
init , cont , err := ClientDeferred ( machineKey , controlKey , protocolVersion )
if err != nil {
if err != nil {
return nil , err
return nil , err
}
}
@ -118,7 +118,7 @@ func Client(ctx context.Context, conn net.Conn, machineKey key.MachinePrivate, c
return cont ( ctx , conn )
return cont ( ctx , conn )
}
}
func continueClientHandshake ( ctx context . Context , conn net . Conn , s * symmetricState , machineKey , machineEphemeral key . MachinePrivate , controlKey key . MachinePublic ) ( * Conn , error ) {
func continueClientHandshake ( ctx context . Context , conn net . Conn , s * symmetricState , machineKey , machineEphemeral key . MachinePrivate , controlKey key . MachinePublic , protocolVersion uint16 ) ( * Conn , error ) {
// No matter what, this function can only run once per s. Ensure
// No matter what, this function can only run once per s. Ensure
// attempted reuse causes a panic.
// attempted reuse causes a panic.
defer func ( ) {
defer func ( ) {
@ -193,13 +193,19 @@ func continueClientHandshake(ctx context.Context, conn net.Conn, s *symmetricSta
// Server initiates a control server handshake, returning the resulting
// Server initiates a control server handshake, returning the resulting
// control connection.
// control connection.
//
//
// maxSupportedVersion is the highest handshake version the server is
// willing to handshake with. The server will handshake with any
// version from 0 to maxSupportedVersion inclusive, the caller should
// inspect conn.Version() to determine what version of the handshake
// was executed.
//
// optionalInit can be the client's initial handshake message as
// optionalInit can be the client's initial handshake message as
// returned by ClientDeferred, or nil in which case the initial
// returned by ClientDeferred, or nil in which case the initial
// message is read from conn.
// message is read from conn.
//
//
// The context deadline, if any, covers the entire handshaking
// The context deadline, if any, covers the entire handshaking
// process.
// process.
func Server ( ctx context . Context , conn net . Conn , controlKey key . MachinePrivate , optionalInit [ ] byte ) ( * Conn , error ) {
func Server ( ctx context . Context , conn net . Conn , controlKey key . MachinePrivate , maxSupportedVersion uint16 , optionalInit [ ] byte ) ( * Conn , error ) {
if deadline , ok := ctx . Deadline ( ) ; ok {
if deadline , ok := ctx . Deadline ( ) ; ok {
if err := conn . SetDeadline ( deadline ) ; err != nil {
if err := conn . SetDeadline ( deadline ) ; err != nil {
return nil , fmt . Errorf ( "setting conn deadline: %w" , err )
return nil , fmt . Errorf ( "setting conn deadline: %w" , err )
@ -239,9 +245,16 @@ func Server(ctx context.Context, conn net.Conn, controlKey key.MachinePrivate, o
} else if _ , err := io . ReadFull ( conn , init . Header ( ) ) ; err != nil {
} else if _ , err := io . ReadFull ( conn , init . Header ( ) ) ; err != nil {
return nil , err
return nil , err
}
}
if init . Version ( ) != protocolVersion {
// Currently, these versions exclusively indicate what the upper
return nil , sendErr ( "unsupported protocol version" )
// RPC protocol understands, the Noise handshake is exactly the
// same in all versions. If that ever changes, this check will
// need to become more complex to handle different kinds of
// handshake.
if init . Version ( ) > maxSupportedVersion {
return nil , sendErr ( "unsupported handshake version" )
}
}
// Just a rename to make it more obvious what the value is
clientVersion := init . Version ( )
if init . Type ( ) != msgTypeInitiation {
if init . Type ( ) != msgTypeInitiation {
return nil , sendErr ( "unexpected handshake message type" )
return nil , sendErr ( "unexpected handshake message type" )
}
}
@ -257,7 +270,7 @@ func Server(ctx context.Context, conn net.Conn, controlKey key.MachinePrivate, o
// prologue. Can only do this once we at least think the client is
// prologue. Can only do this once we at least think the client is
// handshaking using a supported version.
// handshaking using a supported version.
s . MixHash ( protocolVersionPrologue ( proto co lVersion) )
s . MixHash ( protocolVersionPrologue ( client Version) )
// <- s
// <- s
// ...
// ...
@ -310,7 +323,7 @@ func Server(ctx context.Context, conn net.Conn, controlKey key.MachinePrivate, o
c := & Conn {
c := & Conn {
conn : conn ,
conn : conn ,
version : proto co lVersion,
version : client Version,
peer : machineKey ,
peer : machineKey ,
handshakeHash : s . h ,
handshakeHash : s . h ,
tx : txState {
tx : txState {