pnyx/client.go

115 lines
2.3 KiB
Go

package pnyx
import (
"crypto/ed25519"
"crypto/rand"
"crypto/sha512"
"net"
"sync"
"fmt"
"github.com/google/uuid"
)
type ClientID uuid.UUID
func(id ClientID) String() string {
return uuid.UUID(id).String()
}
type ClientState uint8
const (
CLIENT_SESSION_CREATE ClientState = iota
CLIENT_SESSION_CONNECT
CLIENT_SESSION_CONNECTED
)
type Client struct {
Key ed25519.PrivateKey
ConnectionLock sync.Mutex
Connection *net.UDPConn
State ClientState
}
func ID[T ~[16]byte, V ~[]byte](data V) T {
hash := sha512.Sum512(data)
return (T)(hash[0:16])
}
func NewClient(key ed25519.PrivateKey) (Client, error) {
if key == nil {
var err error
_, key, err = ed25519.GenerateKey(rand.Reader)
if err != nil {
return Client{}, err
}
}
return Client{
Key: key,
State: CLIENT_SESSION_CREATE,
}, nil
}
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
}
client.Connection, err = net.DialUDP("udp", nil, address)
if err != nil {
return nil, nil, err
}
session_open, ecdh_private, err := NewSessionOpen(client.Key)
if err != nil {
client.Connection.Close()
client.Connection = nil
return nil, nil, err
}
_, err = client.Connection.Write(session_open)
if err != nil {
return nil, nil, err
}
var response = [512]byte{}
read, _, err := client.Connection.ReadFromUDP(response[:])
if err != nil {
return nil, nil, err
}
if response[0] != byte(SESSION_OPEN) {
return nil, nil, fmt.Errorf("Invalid SESSION_OPEN response: %x", response[0])
}
server_public, ecdh_public, err := ParseSessionOpen(response[COMMAND_LENGTH:read])
if err != nil {
return nil, nil, err
}
secret, err := ECDH(ecdh_public, ecdh_private)
if err != nil {
return nil, nil, err
}
client.State = CLIENT_SESSION_CONNECT
session_connect := NewSessionConnect(client.Connection.LocalAddr().(*net.UDPAddr), secret)
_, err = client.Connection.Write(session_connect)
if err != nil {
return nil, nil, err
}
read, _, err = client.Connection.ReadFromUDP(response[:])
if err != nil {
return nil, nil, err
}
return server_public, secret, nil
}