206 lines
5.5 KiB
Go
206 lines
5.5 KiB
Go
package relay
|
|
|
|
import (
|
|
"WhspBrd/menc"
|
|
"WhspBrd/owner"
|
|
"WhspBrd/thrembio"
|
|
"WhspBrd/typio/splco"
|
|
"crypto/mlkem"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"strconv"
|
|
"time"
|
|
)
|
|
|
|
type Client interface {
|
|
Register(tokenId uint32, token []byte) error
|
|
Login() error
|
|
PublishKem() error
|
|
GetKem(whos owner.Identity) (*mlkem.EncapsulationKey1024, error)
|
|
Send(to owner.Identity, encap *mlkem.EncapsulationKey1024, data []byte) error
|
|
MessageLen() uint32
|
|
MessageGet(id uint32) (sender owner.Identity, timestamp uint32, data []byte, err error)
|
|
}
|
|
|
|
type client struct {
|
|
th thrembio.Client
|
|
sc owner.Secret
|
|
spuha owner.Identity
|
|
}
|
|
|
|
func NewClient(addr string, secret owner.Secret, spuha owner.Identity) (Client, error) {
|
|
host, portStr, err := net.SplitHostPort(addr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid address, expected ip:port: %w", err)
|
|
}
|
|
port, err := strconv.Atoi(portStr)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("invalid port: %w", err)
|
|
}
|
|
ip := net.ParseIP(host)
|
|
if ip == nil {
|
|
return nil, fmt.Errorf("invalid ip: %s", host)
|
|
}
|
|
th, err := thrembio.NewClient(&net.UDPAddr{IP: ip, Port: port}, secret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &client{
|
|
th: th,
|
|
sc: secret,
|
|
spuha: spuha,
|
|
}, nil
|
|
}
|
|
|
|
func (c *client) Register(tokenId uint32, token []byte) error {
|
|
return c.th.Register(tokenId, token)
|
|
}
|
|
|
|
func (c *client) Login() error {
|
|
return c.th.Login()
|
|
}
|
|
|
|
func (c *client) PublishKem() error {
|
|
// [serverPuha] [kek] [osign(serverPuha | kek)]
|
|
data := append(c.spuha[:], c.sc.EncapKey()...)
|
|
osign, err := c.sc.Sign(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
data = append(data, osign...)
|
|
data = append([]byte("pubkem"), data...)
|
|
err = c.th.Write(data)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res, err := c.th.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(res) != "done" {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *client) GetKem(whos owner.Identity) (*mlkem.EncapsulationKey1024, error) {
|
|
// [receiverPuha]
|
|
// <- ([serverPuha] [kek] [osign(serverPuha | kek)])(of that receiverPuha)
|
|
err := c.th.Write(append([]byte("getkem"), whos[:]...))
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
res, err := c.th.Read()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if len(res) < owner.IdentitySize+mlkem.EncapsulationKeySize1024 {
|
|
return nil, errors.New("invalid response length")
|
|
}
|
|
serverPuha := res[:owner.IdentitySize]
|
|
kek := res[owner.IdentitySize : owner.IdentitySize+mlkem.EncapsulationKeySize1024]
|
|
osign := res[owner.IdentitySize+mlkem.EncapsulationKeySize1024:]
|
|
whosver, err := owner.Verify(append(serverPuha, kek...), osign)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if whosver != whos {
|
|
return nil, errors.New("not signed by the expected owner")
|
|
}
|
|
return mlkem.NewEncapsulationKey1024(kek)
|
|
}
|
|
|
|
func (c *client) Send(to owner.Identity, encap *mlkem.EncapsulationKey1024, data []byte) error {
|
|
// -> [serverPuha] [receiverPuha] [kct] [time] [encData] [osign(serverPuha | receiverPuha | kct | time | encData)]
|
|
// <- "send"
|
|
shared, kct := encap.Encapsulate()
|
|
key := sha256.Sum256(shared)
|
|
timee := make([]byte, 8)
|
|
binary.BigEndian.PutUint64(timee, uint64(time.Now().Unix()))
|
|
encData, err := menc.AESGCM_Quick_Encrypt(key[:], data, timee)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
payload := splco.Append(c.spuha[:], to[:], kct, timee, encData)
|
|
osign, err := c.sc.Sign(payload)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
payload = append(payload, osign...)
|
|
err = c.th.Write(append([]byte("send"), payload...))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
res, err := c.th.Read()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if string(res) != "send" {
|
|
return errors.New("unexpected response")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *client) MessageLen() uint32 {
|
|
// -> "msglen"
|
|
// <- [len]
|
|
err := c.th.Write([]byte("msglen"))
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
res, err := c.th.Read()
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
if len(res) != 4 {
|
|
return 0
|
|
}
|
|
return binary.BigEndian.Uint32(res)
|
|
}
|
|
|
|
func (c *client) MessageGet(id uint32) (sender owner.Identity, timestamp uint32, data []byte, err error) {
|
|
// -> [id]
|
|
// <- [serverPuha] [receiverPuha] [kct] [time] [encData] [osign(serverPuha | senderPuha | kct | time | encData)]
|
|
idB := make([]byte, 4)
|
|
binary.BigEndian.PutUint32(idB, id)
|
|
err = c.th.Write(append([]byte("msgget"), idB...))
|
|
if err != nil {
|
|
return owner.Identity{}, 0, nil, err
|
|
}
|
|
res, err := c.th.Read()
|
|
if err != nil {
|
|
return owner.Identity{}, 0, nil, err
|
|
}
|
|
if len(res) < owner.IdentitySize*2+mlkem.CiphertextSize1024+8 {
|
|
return owner.Identity{}, 0, nil, errors.New("invalid response length")
|
|
}
|
|
serverPuha := owner.Identity(res[:owner.IdentitySize])
|
|
//receiverPuha := owner.Identity(res[owner.IdentitySize : owner.IdentitySize*2])
|
|
kct := res[owner.IdentitySize*2 : owner.IdentitySize*2+mlkem.CiphertextSize1024]
|
|
timee := res[owner.IdentitySize*2+mlkem.CiphertextSize1024 : owner.IdentitySize*2+mlkem.CiphertextSize1024+8]
|
|
encData := res[owner.IdentitySize*2+mlkem.CiphertextSize1024+8 : owner.IdentitySize*2+mlkem.CiphertextSize1024+8+len(res)-owner.IdentitySize*2-mlkem.CiphertextSize1024-8]
|
|
osign := res[len(res)-owner.IdentitySize:]
|
|
verData := res[:len(res)-owner.IdentitySize]
|
|
whosver, err := owner.Verify(verData, osign)
|
|
if err != nil {
|
|
return owner.Identity{}, 0, nil, err
|
|
}
|
|
shared, err := c.sc.Decapsulate(kct)
|
|
if err != nil {
|
|
return owner.Identity{}, 0, nil, err
|
|
}
|
|
key := sha256.Sum256(shared)
|
|
data, err = menc.AESGCM_Quick_Decrypt(key[:], encData, timee)
|
|
if err != nil {
|
|
return owner.Identity{}, 0, nil, err
|
|
}
|
|
if owner.IdentityEq(c.spuha, serverPuha) {
|
|
return owner.Identity{}, 0, nil, errors.New("unexpected sender")
|
|
}
|
|
return whosver, binary.BigEndian.Uint32(timee), data, nil
|
|
|
|
}
|