From b5901aa3caddd987c5f4b6bc1ab7f62249580fcc Mon Sep 17 00:00:00 2001 From: Sam Anthony Date: Fri, 11 Apr 2025 14:19:12 -0400 Subject: load public key from file --- handshake.go | 5 +++-- key/file.go | 59 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ key/key.go | 29 +++++++++++++++++++++++++++ key/keygen.go | 63 +++++++++++++++-------------------------------------------- main.go | 9 +++++---- 5 files changed, 112 insertions(+), 53 deletions(-) create mode 100644 key/file.go create mode 100644 key/key.go diff --git a/handshake.go b/handshake.go index 9c7f1b2..e47813f 100644 --- a/handshake.go +++ b/handshake.go @@ -2,6 +2,7 @@ package main import ( "golang.org/x/sync/errgroup" + "net" "git.samanthony.xyz/hose/key" ) @@ -12,8 +13,8 @@ import ( func handshake(rhost string) error { logf("initiating handshake with %s...", rhost) var group errgroup.Group - group.Go(handshakeSend(rhost)) - group.Go(handshakeRecv(rhost)) + group.Go(func() error { return handshakeSend(rhost) }) + group.Go(func() error { return handshakeRecv(rhost) }) return group.Wait() } diff --git a/key/file.go b/key/file.go new file mode 100644 index 0000000..f96190a --- /dev/null +++ b/key/file.go @@ -0,0 +1,59 @@ +package key + +import ( + "errors" + "fmt" + "github.com/adrg/xdg" + "os" + "path/filepath" +) + +var ( + pubKeyFile = filepath.Join(xdg.DataHome, "hose", "pubkey") + pubKeyFileMode os.FileMode = 0644 + + privKeyFile = filepath.Join(xdg.DataHome, "hose", "privkey") + privKeyFileMode os.FileMode = 0600 +) + +// createFile creates a file with the specified permissions and returns it for writing. +// It does not truncate an existing file. If the file already exists, an error is returned. +func createFile(name string, mode os.FileMode) (*os.File, error) { + exists, err := fileExists(name) + if err != nil { + return nil, err // unexpected error. + } else if exists { + return nil, errFileExists(name) // file exists; do not overwrite. + } + // Does not exist; continue; + + f, err := os.Create(name) + if err != nil { + return nil, err + } + + if err := f.Chmod(mode); err != nil { + f.Close() + _ = os.Remove(name) + return nil, err + } + + return f, nil +} + +// fileExists returns a nil error and true/false if a file does/doesn't exist. +// If an error is encountered, a non-nil error is returned. +func fileExists(path string) (bool, error) { + _, err := os.Stat(path) + if errors.Is(err, os.ErrNotExist) { + return false, nil // file doesn't exist. + } else if err != nil { + return false, err // unexpected error. + } + return true, nil // file exists. +} + +// errFileExists constructs a 'file already exists' error message. +func errFileExists(path string) error { + return fmt.Errorf("%s: %s", os.ErrExist, path) +} diff --git a/key/key.go b/key/key.go new file mode 100644 index 0000000..a427769 --- /dev/null +++ b/key/key.go @@ -0,0 +1,29 @@ +package key + +import ( + "io" + "os" +) + +// LoadPublicKey reads the public key from disc, or generates a new keypair +// if it does not already exist. +func LoadPublicKey() ([32]byte, error) { + // Generate a keypair if it doesn't already exist. + if err := generateIfNoExist(); err != nil { + return [32]byte{}, err + } + + // Open key file. + f, err := os.Open(pubKeyFile) + if err != nil { + return [32]byte{}, err + } + defer f.Close() + + // Read key. + var pubkey [32]byte + if _, err := io.ReadFull(f, pubkey[:]); err != nil { + return [32]byte{}, err + } + return pubkey, nil +} diff --git a/key/keygen.go b/key/keygen.go index 9857b76..813df31 100644 --- a/key/keygen.go +++ b/key/keygen.go @@ -2,21 +2,9 @@ package key import ( crypto_rand "crypto/rand" - "errors" "fmt" - "os" - "path/filepath" - - "github.com/adrg/xdg" "golang.org/x/crypto/nacl/box" -) - -var ( - pubKeyFile = filepath.Join(xdg.DataHome, "hose", "pubkey") - pubKeyFileMode os.FileMode = 0644 - - privKeyFile = filepath.Join(xdg.DataHome, "hose", "privkey") - privKeyFileMode os.FileMode = 0600 + "os" ) // Generate generates a new public/private keypair. It stores the private key in the @@ -56,44 +44,25 @@ func Generate() error { return nil } -// createFile creates a file with the specified permissions and returns it for writing. -// It does not truncate an existing file. If the file already exists, an error is returned. -func createFile(name string, mode os.FileMode) (*os.File, error) { - exists, err := fileExists(name) +// Generate a keypair if it doesn't already exist. +func generateIfNoExist() error { + pubExists, err := fileExists(pubKeyFile) if err != nil { - return nil, err // unexpected error. - } else if exists { - return nil, errFileExists(name) // file exists; do not overwrite. + return err } - // Does not exist; continue; - - f, err := os.Create(name) + privExists, err := fileExists(privKeyFile) if err != nil { - return nil, err + return err } - if err := f.Chmod(mode); err != nil { - f.Close() - _ = os.Remove(name) - return nil, err + if pubExists && privExists { + // Keypair already exists. + return nil + } else if pubExists && !privExists { + return fmt.Errorf("found public key file but not private key file") + } else if privExists && !pubExists { + return fmt.Errorf("found private key file but not public key file") } - - return f, nil -} - -// fileExists returns a nil error and true/false if a file does/doesn't exist. -// If an error is encountered, a non-nil error is returned. -func fileExists(path string) (bool, error) { - _, err := os.Stat(path) - if errors.Is(err, os.ErrNotExist) { - return false, nil // file doesn't exist. - } else if err != nil { - return false, err // unexpected error. - } - return true, nil // file exists. -} - -// errFileExists constructs a 'file already exists' error message. -func errFileExists(path string) error { - return fmt.Errorf("%s: %s", os.ErrExist, path) + // Neither public nor private key file exists; generate new keypair. + return Generate() } diff --git a/main.go b/main.go index 906bfb6..66ea5bd 100644 --- a/main.go +++ b/main.go @@ -12,17 +12,18 @@ import ( const ( port = "60321" network = "tcp" - usage = "Usage: hose <-r | -s >" + usage = "Usage: hose <-handshake | -r | -s >" ) var ( - recvFlag = flag.Bool("r", false, "receive") - sendHost = flag.String("s", "", "send to remote host") + handshakeHost = flag.String("handshake", "", "exchange public keys with remote host") + recvFlag = flag.Bool("r", false, "receive") + sendHost = flag.String("s", "", "send to remote host") ) func main() { flag.Parse() - if *handshakeHost { + if *handshakeHost != "" { if err := handshake(*handshakeHost); err != nil { eprintf("%v\n", err) } -- cgit v1.2.3