diff options
| author | Sam Anthony <sam@samanthony.xyz> | 2025-04-11 12:40:54 -0400 |
|---|---|---|
| committer | Sam Anthony <sam@samanthony.xyz> | 2025-04-11 12:40:54 -0400 |
| commit | 93610fb8d1640763f0cd4ae5d465d8377696c273 (patch) | |
| tree | 0fd87f6f0adf534303265bba768dc85bbe224c3c /key | |
| parent | b2695533495d5d467625ddaf67d5851c7bea1247 (diff) | |
| download | hose-93610fb8d1640763f0cd4ae5d465d8377696c273.zip | |
generate keypair
Diffstat (limited to 'key')
| -rw-r--r-- | key/keygen.go | 99 |
1 files changed, 99 insertions, 0 deletions
diff --git a/key/keygen.go b/key/keygen.go new file mode 100644 index 0000000..9857b76 --- /dev/null +++ b/key/keygen.go @@ -0,0 +1,99 @@ +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 +) + +// Generate generates a new public/private keypair. It stores the private key in the +// private key file and the public key in the public key file. If either of the key +// files already exist, they will not be overwritten; instead an error will be returned. +func Generate() error { + // Create public key file. + pubFile, err := createFile(pubKeyFile, pubKeyFileMode) + if err != nil { + return err + } + defer pubFile.Close() + + // Create private key file. + privFile, err := createFile(privKeyFile, privKeyFileMode) + if err != nil { + pubFile.Close() + _ = os.Remove(pubKeyFile) + return err + } + defer privFile.Close() + + // Generate keypair. + pubkey, privkey, err := box.GenerateKey(crypto_rand.Reader) + if err != nil { + return err + } + + // Write keypair to files. + if _, err := pubFile.Write((*pubkey)[:]); err != nil { + return err + } + if _, err := privFile.Write((*privkey)[:]); err != nil { + return err + } + + 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) + 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) +} |