|
|
|
@ -3,7 +3,10 @@ package pnyx
|
|
|
|
|
import (
|
|
|
|
|
"crypto/ed25519"
|
|
|
|
|
"crypto/rand"
|
|
|
|
|
mrand "math/rand"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"crypto/sha512"
|
|
|
|
|
"crypto/aes"
|
|
|
|
|
"net"
|
|
|
|
|
"sync"
|
|
|
|
|
"fmt"
|
|
|
|
@ -26,9 +29,9 @@ const (
|
|
|
|
|
|
|
|
|
|
type Client struct {
|
|
|
|
|
Key ed25519.PrivateKey
|
|
|
|
|
Session Session
|
|
|
|
|
ConnectionLock sync.Mutex
|
|
|
|
|
Connection *net.UDPConn
|
|
|
|
|
State ClientState
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func ID[T ~[16]byte, V ~[]byte](data V) T {
|
|
|
|
@ -36,79 +39,90 @@ func ID[T ~[16]byte, V ~[]byte](data V) T {
|
|
|
|
|
return (T)(hash[0:16])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func NewClient(key ed25519.PrivateKey) (Client, error) {
|
|
|
|
|
func NewClient(key ed25519.PrivateKey, remote string) (*Client, error) {
|
|
|
|
|
if key == nil {
|
|
|
|
|
var err error
|
|
|
|
|
_, key, err = ed25519.GenerateKey(rand.Reader)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return Client{}, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return Client{
|
|
|
|
|
Key: key,
|
|
|
|
|
State: CLIENT_SESSION_CREATE,
|
|
|
|
|
}, nil
|
|
|
|
|
seed_bytes := make([]byte, 8)
|
|
|
|
|
read, err := rand.Read(seed_bytes)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
} else if read != 8 {
|
|
|
|
|
return nil, fmt.Errorf("Failed to create IV seed for client")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func(client *Client) Connect(remote string) (ed25519.PublicKey, []byte, error) {
|
|
|
|
|
client.ConnectionLock.Lock()
|
|
|
|
|
defer client.ConnectionLock.Unlock()
|
|
|
|
|
|
|
|
|
|
address, err := net.ResolveUDPAddr("udp", remote)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client.Connection, err = net.DialUDP("udp", nil, address)
|
|
|
|
|
connection, err := net.DialUDP("udp", nil, address)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
session_open, ecdh_private, err := NewSessionOpen(client.Key)
|
|
|
|
|
session_open, ecdh_private, err := NewSessionOpen(key)
|
|
|
|
|
if err != nil {
|
|
|
|
|
client.Connection.Close()
|
|
|
|
|
client.Connection = nil
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
connection.Close()
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = client.Connection.Write(session_open)
|
|
|
|
|
_, err = connection.Write(session_open)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var response = [512]byte{}
|
|
|
|
|
read, _, err := client.Connection.ReadFromUDP(response[:])
|
|
|
|
|
read, _, err = connection.ReadFromUDP(response[:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if response[0] != byte(SESSION_OPEN) {
|
|
|
|
|
return nil, nil, fmt.Errorf("Invalid SESSION_OPEN response: %x", response[0])
|
|
|
|
|
return nil, fmt.Errorf("Invalid SESSION_OPEN response: %x", response[0])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
server_public, ecdh_public, err := ParseSessionOpen(response[COMMAND_LENGTH:read])
|
|
|
|
|
server_pubkey, ecdh_public, err := ParseSessionOpen(response[COMMAND_LENGTH:read])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
secret, err := ECDH(ecdh_public, ecdh_private)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
client.State = CLIENT_SESSION_CONNECT
|
|
|
|
|
session_cipher, err := aes.NewCipher(secret)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
session_connect := NewSessionConnect(client.Connection.LocalAddr().(*net.UDPAddr), secret)
|
|
|
|
|
_, err = client.Connection.Write(session_connect)
|
|
|
|
|
session_connect := NewSessionConnect(connection.LocalAddr().(*net.UDPAddr), secret)
|
|
|
|
|
_, err = connection.Write(session_connect)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
read, _, err = client.Connection.ReadFromUDP(response[:])
|
|
|
|
|
read, _, err = connection.ReadFromUDP(response[:])
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, nil, err
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return server_public, secret, nil
|
|
|
|
|
return &Client{
|
|
|
|
|
Key: key,
|
|
|
|
|
Session: Session{
|
|
|
|
|
ID: ID[SessionID](secret),
|
|
|
|
|
remote: address,
|
|
|
|
|
Peer: ID[ClientID](server_pubkey),
|
|
|
|
|
secret: secret,
|
|
|
|
|
cipher: session_cipher,
|
|
|
|
|
iv_generator: mrand.NewSource(int64(binary.BigEndian.Uint64(seed_bytes))).(mrand.Source64),
|
|
|
|
|
},
|
|
|
|
|
Connection: connection,
|
|
|
|
|
}, nil
|
|
|
|
|
}
|
|
|
|
|