register, register device and get devices working.
This commit is contained in:
parent
a8b497d426
commit
320dc46010
14 changed files with 405 additions and 65 deletions
39
cmd/cli/getDevices.go
Normal file
39
cmd/cli/getDevices.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"os"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/configfile"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(getDevicesCmd)
|
||||
}
|
||||
|
||||
var getDevicesCmd = &cobra.Command{
|
||||
Use: "get-devices",
|
||||
Short: "Register a user with a given email and password",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cc, err := configfile.EnsureAndGet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cc.DeviceId == "" {
|
||||
return errors.New("your device is not registered")
|
||||
}
|
||||
pvk, err := configfile.LoadPrivateKey()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
devices, err := apiclient.GetDevices(cc.DeviceId, pvk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return json.NewEncoder(os.Stdout).Encode(devices)
|
||||
},
|
||||
}
|
||||
|
|
@ -2,38 +2,20 @@ package main
|
|||
|
||||
import (
|
||||
"encoding/base64"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/server/client"
|
||||
)
|
||||
|
||||
func b64(i []byte) string {
|
||||
return base64.StdEncoding.EncodeToString(i)
|
||||
}
|
||||
|
||||
var (
|
||||
priv1 = "~/dev/ccclip/keys1/private.key"
|
||||
pub1 = "~/dev/ccclip/keys1/public.key"
|
||||
var apiclient *client.Client
|
||||
|
||||
priv2 = "~/dev/ccclip/keys2/private.key"
|
||||
pub2 = "~/dev/ccclip/keys2/public.key"
|
||||
)
|
||||
func init() {
|
||||
apiclient = client.New("http://localhost:8080")
|
||||
}
|
||||
|
||||
func main() {
|
||||
rootCmd.Execute()
|
||||
// key1 := crypto.LoadPrivateKey("../keys1/private.key")
|
||||
// key2 := crypto.LoadPrivateKey("../keys2/private.key")
|
||||
|
||||
// secretMsg := "new-some-secret-messageeee"
|
||||
|
||||
// encrypted := crypto.Encrypt(
|
||||
// crypto.NewSharedKey(key1, key2.PublicKey(), crypto.SendDirection),
|
||||
// []byte(secretMsg),
|
||||
// )
|
||||
|
||||
// fmt.Printf("Message %q was encrypted to %q\n", secretMsg, b64(encrypted))
|
||||
|
||||
// decrypted := crypto.Decrypt(
|
||||
// crypto.NewSharedKey(key2, key1.PublicKey(), crypto.ReceiveDirection),
|
||||
// encrypted,
|
||||
// )
|
||||
|
||||
// fmt.Printf("Message was decrypted as %q\n", string(decrypted))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,14 +1,11 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"fmt"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/server"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/configfile"
|
||||
)
|
||||
|
||||
var email string
|
||||
|
|
@ -28,25 +25,16 @@ var registerCmd = &cobra.Command{
|
|||
Use: "register",
|
||||
Short: "Register a user with a given email and password",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
req := server.RegisterRequest{
|
||||
Email: email,
|
||||
Password: password,
|
||||
}
|
||||
reqJson, err := json.Marshal(req)
|
||||
err := apiclient.Register(email, password)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := http.Post("http://localhost:8080/register", "application/json", bytes.NewReader(reqJson))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
resBody, err := io.ReadAll(res.Body)
|
||||
defer res.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("could not register user: %w", err)
|
||||
}
|
||||
|
||||
log.Println(string(resBody))
|
||||
return nil
|
||||
cc, err := configfile.EnsureAndGet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
cc.Email = email
|
||||
return configfile.Write(cc)
|
||||
},
|
||||
}
|
||||
|
|
|
|||
58
cmd/cli/registerDevice.go
Normal file
58
cmd/cli/registerDevice.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/configfile"
|
||||
"github.com/AYM1607/ccclip/pkg/crypto"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(registerDeviceCommand)
|
||||
|
||||
registerDeviceCommand.Flags().StringVarP(&password, "password", "p", "", "password for your account")
|
||||
|
||||
registerDeviceCommand.MarkFlagRequired("password")
|
||||
}
|
||||
|
||||
var registerDeviceCommand = &cobra.Command{
|
||||
Use: "register-device",
|
||||
Short: "Register a device for the given user",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
cc, err := configfile.EnsureAndGet()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if cc.Email == "" {
|
||||
return errors.New("you don't have an account configured for thist device")
|
||||
}
|
||||
|
||||
if cc.DeviceId != "" {
|
||||
return errors.New("this device is already registered")
|
||||
}
|
||||
|
||||
pvk := crypto.NewPrivateKey()
|
||||
pbk := pvk.PublicKey()
|
||||
|
||||
res, err := apiclient.RegisterDevice(cc.Email, password, pbk.Bytes())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Write the key files first, if those fail to write then we should not
|
||||
// save the device Id.
|
||||
cc.DeviceId = res.DeviceID
|
||||
err = configfile.SavePrivateKey(pvk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = configfile.SavePublicKey(pbk)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return configfile.Write(cc)
|
||||
},
|
||||
}
|
||||
|
|
@ -3,11 +3,10 @@ package main
|
|||
import (
|
||||
"log"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/configfile"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var keyset int
|
||||
|
||||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "ccclip",
|
||||
|
|
@ -19,9 +18,9 @@ var rootCmd = &cobra.Command{
|
|||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.PersistentFlags().IntVarP(&keyset, "keyset", "k", 0, "which key set to use, can be 1 or 2")
|
||||
rootCmd.PersistentFlags().StringVarP(&configfile.Path, "config-path", "c", "", "directory where to store the config file")
|
||||
|
||||
rootCmd.MarkPersistentFlagRequired("keyset")
|
||||
rootCmd.MarkPersistentFlagRequired("config-path")
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
|
|
|
|||
1
cmd/cli/util.go
Normal file
1
cmd/cli/util.go
Normal file
|
|
@ -0,0 +1 @@
|
|||
package main
|
||||
1
go.mod
1
go.mod
|
|
@ -8,6 +8,7 @@ require (
|
|||
github.com/spf13/cobra v1.8.0
|
||||
golang.org/x/crypto v0.14.0
|
||||
golang.org/x/term v0.13.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
3
go.sum
3
go.sum
|
|
@ -17,5 +17,8 @@ golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
|||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.13.0 h1:bb+I9cTfFazGW51MZqBVmZy7+JEJMouUHTUSKVQLBek=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
|||
74
internal/configfile/config.go
Normal file
74
internal/configfile/config.go
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
package configfile
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/AYM1607/ccclip/pkg/crypto"
|
||||
)
|
||||
|
||||
const FileName = "ccclip.yaml"
|
||||
const PrivateKeyFileName = "private.key"
|
||||
const PublicKeyFileName = "public.key"
|
||||
|
||||
type ConfigFile struct {
|
||||
Email string `yaml:"email"`
|
||||
DeviceId string `yaml:"deviceId"`
|
||||
}
|
||||
|
||||
var Path string
|
||||
|
||||
func EnsureAndGet() (ConfigFile, error) {
|
||||
err := os.MkdirAll(Path, os.FileMode(int(0660)))
|
||||
if err != nil {
|
||||
return ConfigFile{}, fmt.Errorf("could not create config directory: %w", err)
|
||||
}
|
||||
rawC, err := os.ReadFile(path.Join(Path, FileName))
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return ConfigFile{}, nil
|
||||
}
|
||||
return ConfigFile{}, fmt.Errorf("could not read current config file: %w", err)
|
||||
}
|
||||
var c ConfigFile
|
||||
return c, json.Unmarshal(rawC, &c)
|
||||
}
|
||||
|
||||
func Write(c ConfigFile) error {
|
||||
err := os.MkdirAll(Path, os.FileMode(int(0660)))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rawC, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not convert config to json")
|
||||
}
|
||||
err = os.WriteFile(path.Join(Path, FileName), rawC, os.FileMode(int(0660)))
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not write file to config directory: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func LoadPrivateKey() (*ecdh.PrivateKey, error) {
|
||||
fp := path.Join(Path, PrivateKeyFileName)
|
||||
return crypto.LoadPrivateKey(fp)
|
||||
}
|
||||
|
||||
func LoadPublicKey() (*ecdh.PublicKey, error) {
|
||||
fp := path.Join(Path, PublicKeyFileName)
|
||||
return crypto.LoadPublicKey(fp)
|
||||
}
|
||||
|
||||
func SavePrivateKey(k *ecdh.PrivateKey) error {
|
||||
fp := path.Join(Path, PrivateKeyFileName)
|
||||
return crypto.SavePrivateKey(fp, k)
|
||||
}
|
||||
|
||||
func SavePublicKey(k *ecdh.PublicKey) error {
|
||||
fp := path.Join(Path, PublicKeyFileName)
|
||||
return crypto.SavePublicKey(fp, k)
|
||||
}
|
||||
131
internal/server/client/client.go
Normal file
131
internal/server/client/client.go
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/ecdh"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/server"
|
||||
"github.com/AYM1607/ccclip/pkg/crypto"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
url string
|
||||
}
|
||||
|
||||
func New(url string) *Client {
|
||||
return &Client{
|
||||
url: url,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) Register(email, password string) error {
|
||||
req := server.RegisterRequest{
|
||||
Email: email,
|
||||
Password: password,
|
||||
}
|
||||
|
||||
reqJson, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
res, err := http.Post(c.url+"/register", "application/json", bytes.NewReader(reqJson))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res.StatusCode != http.StatusCreated {
|
||||
return errors.New("got unexpected response code from server")
|
||||
}
|
||||
|
||||
resBody, err := io.ReadAll(res.Body)
|
||||
defer res.Body.Close()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
log.Println(string(resBody))
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) RegisterDevice(email, password string, devicePublicKey []byte) (*server.RegisterDeviceResponse, error) {
|
||||
req := server.RegisterDeviceRequest{
|
||||
Email: email,
|
||||
Password: password,
|
||||
PublicKey: devicePublicKey,
|
||||
}
|
||||
reqJson, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hres, err := http.Post(c.url+"/registerDevice", "application/json", bytes.NewReader(reqJson))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if hres.StatusCode != http.StatusCreated {
|
||||
return nil, errors.New("got unexpected response code from server")
|
||||
}
|
||||
|
||||
hresBody, err := io.ReadAll(hres.Body)
|
||||
defer hres.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println(string(hresBody))
|
||||
|
||||
var res server.RegisterDeviceResponse
|
||||
err = json.Unmarshal(hresBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
|
||||
func (c *Client) GetDevices(deviceId string, pvk *ecdh.PrivateKey) (*server.GetUserDevicesResponse, error) {
|
||||
req := server.GetUserDevicesRequest{
|
||||
FingerPrint: server.FingerPrint{Timestamp: time.Now().UTC()},
|
||||
}
|
||||
reqBytes, err := json.Marshal(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
key := crypto.NewSharedKey(pvk, serverPublicKey, crypto.SendDirection)
|
||||
encryptedReq := crypto.Encrypt(key, reqBytes)
|
||||
|
||||
authReq := server.AuthenticatedPayload{
|
||||
DeviceID: deviceId,
|
||||
Payload: encryptedReq,
|
||||
}
|
||||
authReqJson, err := json.Marshal(authReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hres, err := http.Post(c.url+"/userDevices", "application/json", bytes.NewReader(authReqJson))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hresBody, err := io.ReadAll(hres.Body)
|
||||
defer hres.Body.Close()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
log.Println(string(hresBody))
|
||||
|
||||
if hres.StatusCode != http.StatusOK {
|
||||
return nil, errors.New("got unexpected response code from server")
|
||||
}
|
||||
|
||||
var res server.GetUserDevicesResponse
|
||||
err = json.Unmarshal(hresBody, &res)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &res, nil
|
||||
}
|
||||
22
internal/server/client/key.go
Normal file
22
internal/server/client/key.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"crypto/ecdh"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
|
||||
"github.com/AYM1607/ccclip/pkg/crypto"
|
||||
)
|
||||
|
||||
const serverPublicKeyB64 = "JTyaIVDHe1Nwqmd4NFlkvqj+MZOVp5s3JZP+T3QuoT8="
|
||||
|
||||
var serverPublicKey *ecdh.PublicKey
|
||||
|
||||
func init() {
|
||||
pkeyBytes := make([]byte, crypto.KeySize)
|
||||
_, err := base64.StdEncoding.Decode(pkeyBytes, []byte(serverPublicKeyB64))
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("cannot decode server public key: %s", err.Error()))
|
||||
}
|
||||
serverPublicKey = crypto.PublicKeyFromBytes(pkeyBytes)
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package server
|
|||
import (
|
||||
"crypto/ecdh"
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/AYM1607/ccclip/internal/db"
|
||||
|
|
@ -22,6 +23,11 @@ func decryptAuthenticatedPayload[T any](p AuthenticatedPayload, d db.DB, pk *ecd
|
|||
var res T
|
||||
var zero T
|
||||
|
||||
_T := reflect.TypeOf(res)
|
||||
if _T.Kind() == reflect.Pointer {
|
||||
res = reflect.New(_T.Elem()).Interface().(T)
|
||||
}
|
||||
|
||||
device, err := d.GetDevice(p.DeviceID)
|
||||
if err != nil {
|
||||
return zero, err
|
||||
|
|
|
|||
|
|
@ -33,10 +33,18 @@ type controller struct {
|
|||
func newHttpHandler() http.Handler {
|
||||
r := mux.NewRouter()
|
||||
|
||||
pbk, err := crypto.LoadPublicKey(config.Default.PublicKeyPath)
|
||||
if err != nil {
|
||||
panic("could not load server's public key")
|
||||
}
|
||||
pvk, err := crypto.LoadPrivateKey(config.Default.PrivateKeyPath)
|
||||
if err != nil {
|
||||
panic("could not load server's private key")
|
||||
}
|
||||
c := &controller{
|
||||
store: db.NewLocalDB(),
|
||||
publicKey: crypto.LoadPublicKey(config.Default.PublicKeyPath),
|
||||
privateKey: crypto.LoadPrivateKey(config.Default.PrivateKeyPath),
|
||||
publicKey: pbk,
|
||||
privateKey: pvk,
|
||||
}
|
||||
|
||||
// TODO: These are not restful at all, but it's the simplest for now. FIX IT!
|
||||
|
|
|
|||
|
|
@ -19,6 +19,11 @@ const (
|
|||
ReceiveDirection
|
||||
)
|
||||
|
||||
var (
|
||||
privateKeyFileMode = os.FileMode(int(0600))
|
||||
publicKeyFileMode = os.FileMode(int(0644))
|
||||
)
|
||||
|
||||
func NewPrivateKey() *ecdh.PrivateKey {
|
||||
c := ecdh.X25519()
|
||||
k, err := c.GenerateKey(rand.Reader)
|
||||
|
|
@ -75,21 +80,44 @@ func PublicKeyFromBytes(keyBytes []byte) *ecdh.PublicKey {
|
|||
return key
|
||||
}
|
||||
|
||||
func LoadPrivateKey(fp string) *ecdh.PrivateKey {
|
||||
return PrivateKeyFromBytes(loadKey(fp))
|
||||
}
|
||||
|
||||
func LoadPublicKey(fp string) *ecdh.PublicKey {
|
||||
return PublicKeyFromBytes(loadKey(fp))
|
||||
}
|
||||
|
||||
func loadKey(fn string) []byte {
|
||||
b64Key, err := os.ReadFile(fn)
|
||||
func LoadPrivateKey(fp string) (*ecdh.PrivateKey, error) {
|
||||
kb, err := loadKey(fp)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
return nil, err
|
||||
}
|
||||
return PrivateKeyFromBytes(kb), nil
|
||||
}
|
||||
|
||||
func LoadPublicKey(fp string) (*ecdh.PublicKey, error) {
|
||||
kb, err := loadKey(fp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return PublicKeyFromBytes(kb), nil
|
||||
}
|
||||
|
||||
func SavePrivateKey(fp string, k *ecdh.PrivateKey) error {
|
||||
return saveKey(fp, k.Bytes(), privateKeyFileMode)
|
||||
}
|
||||
|
||||
func SavePublicKey(fp string, k *ecdh.PublicKey) error {
|
||||
return saveKey(fp, k.Bytes(), publicKeyFileMode)
|
||||
}
|
||||
|
||||
func loadKey(fp string) ([]byte, error) {
|
||||
b64Key, err := os.ReadFile(fp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
keyBytes := make([]byte, KeySize)
|
||||
base64.StdEncoding.Decode(keyBytes, b64Key)
|
||||
return keyBytes
|
||||
return keyBytes, nil
|
||||
}
|
||||
|
||||
func saveKey(fp string, key []byte, fm os.FileMode) error {
|
||||
b64Key := make([]byte, base64.StdEncoding.EncodedLen(len(key)))
|
||||
base64.StdEncoding.Encode(b64Key, key)
|
||||
|
||||
return os.WriteFile(fp, b64Key, fm)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue