diff --git a/cmd/stbak/cmd/keygen.go b/cmd/stbak/cmd/keygen.go index fe9b47c..238703f 100644 --- a/cmd/stbak/cmd/keygen.go +++ b/cmd/stbak/cmd/keygen.go @@ -1,18 +1,12 @@ package cmd import ( - "bytes" - "crypto/rand" - "io" "io/ioutil" "os" "path/filepath" - "aead.dev/minisign" - "filippo.io/age" - "github.com/ProtonMail/gopenpgp/v2/armor" - "github.com/ProtonMail/gopenpgp/v2/crypto" - "github.com/ProtonMail/gopenpgp/v2/helper" + "github.com/pojntfx/stfs/pkg/config" + "github.com/pojntfx/stfs/pkg/utility" "github.com/spf13/cobra" "github.com/spf13/viper" "github.com/volatiletech/sqlboiler/v4/boil" @@ -31,27 +25,18 @@ var keygenCmd = &cobra.Command{ boil.DebugMode = true } - var privkey []byte - var pubkey []byte - - if encryptionFormat := viper.GetString(encryptionFlag); encryptionFormat != noneKey { - priv, pub, err := generateEncryptionKey(encryptionFormat, viper.GetString(passwordFlag)) - if err != nil { - return err - } - - privkey = priv - pubkey = pub - } else if signatureFormat := viper.GetString(signatureFlag); signatureFormat != noneKey { - priv, pub, err := generateSignatureKey(signatureFormat, viper.GetString(passwordFlag)) - if err != nil { - return err - } - - privkey = priv - pubkey = pub - } else { - return errKeygenForFormatUnsupported + pubkey, privkey, err := utility.Keygen( + config.PipeConfig{ + Compression: viper.GetString(compressionFlag), + Encryption: viper.GetString(encryptionFlag), + Signature: viper.GetString(signatureFlag), + }, + utility.PasswordConfig{ + Password: viper.GetString(passwordFlag), + }, + ) + if err != nil { + return err } // Write pubkey (read/writable by everyone) @@ -72,100 +57,6 @@ var keygenCmd = &cobra.Command{ }, } -func generateEncryptionKey( - encryptionFormat string, - password string, -) (privkey []byte, pubkey []byte, err error) { - switch encryptionFormat { - case encryptionFormatAgeKey: - identity, err := age.GenerateX25519Identity() - if err != nil { - return []byte{}, []byte{}, err - } - - pub := identity.Recipient().String() - priv := identity.String() - - if password != "" { - passwordRecipient, err := age.NewScryptRecipient(password) - if err != nil { - return []byte{}, []byte{}, err - } - - out := &bytes.Buffer{} - w, err := age.Encrypt(out, passwordRecipient) - if err != nil { - return []byte{}, []byte{}, err - } - - if _, err := io.WriteString(w, priv); err != nil { - return []byte{}, []byte{}, err - } - - if err := w.Close(); err != nil { - return []byte{}, []byte{}, err - } - - priv = out.String() - } - - return []byte(priv), []byte(pub), nil - case encryptionFormatPGPKey: - armoredIdentity, err := helper.GenerateKey("STFS", "stfs@example.com", []byte(password), "x25519", 0) - if err != nil { - return []byte{}, []byte{}, err - } - - rawIdentity, err := armor.Unarmor(armoredIdentity) - if err != nil { - return []byte{}, []byte{}, err - } - - identity, err := crypto.NewKey([]byte(rawIdentity)) - if err != nil { - return []byte{}, []byte{}, err - } - - pub, err := identity.GetPublicKey() - if err != nil { - return []byte{}, []byte{}, err - } - - priv, err := identity.Serialize() - if err != nil { - return []byte{}, []byte{}, err - } - - return priv, pub, nil - default: - return []byte{}, []byte{}, errKeygenForFormatUnsupported - } -} - -func generateSignatureKey( - signatureFormat string, - password string, -) (privkey []byte, pubkey []byte, err error) { - switch signatureFormat { - case signatureFormatMinisignKey: - pub, rawPriv, err := minisign.GenerateKey(rand.Reader) - if err != nil { - return []byte{}, []byte{}, err - } - - priv, err := minisign.EncryptKey(password, rawPriv) - if err != nil { - return []byte{}, []byte{}, err - } - - return priv, []byte(pub.String()), err - case signatureFormatPGPKey: - return generateEncryptionKey(signatureFormat, password) - default: - return []byte{}, []byte{}, errKeygenForFormatUnsupported - } -} - func init() { keygenCmd.PersistentFlags().StringP(recipientFlag, "r", "", "Path to write the public key to") keygenCmd.PersistentFlags().StringP(identityFlag, "i", "", "Path to write the private key to") diff --git a/pkg/config/error.go b/pkg/config/error.go index f5a9e7d..3e1f887 100644 --- a/pkg/config/error.go +++ b/pkg/config/error.go @@ -15,4 +15,6 @@ var ( ErrSignatureFormatOnlyRegularSupport = errors.New("this signature format only supports regular files, not i.e. tape drives") ErrSignatureInvalid = errors.New("signature invalid") ErrSignatureMissing = errors.New("signature missing") + + ErrKeygenForFormatUnsupported = errors.New("can not generate keys for this format") ) diff --git a/pkg/utility/config.go b/pkg/utility/config.go new file mode 100644 index 0000000..c3e47fa --- /dev/null +++ b/pkg/utility/config.go @@ -0,0 +1,5 @@ +package utility + +type PasswordConfig struct { + Password string +} diff --git a/pkg/utility/keygen.go b/pkg/utility/keygen.go index 46c96b6..210833f 100644 --- a/pkg/utility/keygen.go +++ b/pkg/utility/keygen.go @@ -1,8 +1,135 @@ package utility -import "github.com/pojntfx/stfs/pkg/config" +import ( + "bytes" + "crypto/rand" + "io" + + "aead.dev/minisign" + "filippo.io/age" + "github.com/ProtonMail/gopenpgp/v2/armor" + "github.com/ProtonMail/gopenpgp/v2/crypto" + "github.com/ProtonMail/gopenpgp/v2/helper" + "github.com/pojntfx/stfs/pkg/config" +) func Keygen( pipes config.PipeConfig, - crypto config.CryptoConfig, -) (privkey []byte, pubkey []byte, err error) + password PasswordConfig, +) (privkey []byte, pubkey []byte, err error) { + if pipes.Encryption != config.NoneKey { + priv, pub, err := generateEncryptionKey(pipes.Encryption, password.Password) + if err != nil { + return []byte{}, []byte{}, err + } + + privkey = priv + pubkey = pub + } else if pipes.Signature != config.NoneKey { + priv, pub, err := generateSignatureKey(pipes.Signature, password.Password) + if err != nil { + return []byte{}, []byte{}, err + } + + privkey = priv + pubkey = pub + } else { + return []byte{}, []byte{}, config.ErrKeygenForFormatUnsupported + } + + return privkey, pubkey, nil +} + +func generateEncryptionKey( + encryptionFormat string, + password string, +) (privkey []byte, pubkey []byte, err error) { + switch encryptionFormat { + case config.EncryptionFormatAgeKey: + identity, err := age.GenerateX25519Identity() + if err != nil { + return []byte{}, []byte{}, err + } + + pub := identity.Recipient().String() + priv := identity.String() + + if password != "" { + passwordRecipient, err := age.NewScryptRecipient(password) + if err != nil { + return []byte{}, []byte{}, err + } + + out := &bytes.Buffer{} + w, err := age.Encrypt(out, passwordRecipient) + if err != nil { + return []byte{}, []byte{}, err + } + + if _, err := io.WriteString(w, priv); err != nil { + return []byte{}, []byte{}, err + } + + if err := w.Close(); err != nil { + return []byte{}, []byte{}, err + } + + priv = out.String() + } + + return []byte(priv), []byte(pub), nil + case config.EncryptionFormatPGPKey: + armoredIdentity, err := helper.GenerateKey("STFS", "stfs@example.com", []byte(password), "x25519", 0) + if err != nil { + return []byte{}, []byte{}, err + } + + rawIdentity, err := armor.Unarmor(armoredIdentity) + if err != nil { + return []byte{}, []byte{}, err + } + + identity, err := crypto.NewKey([]byte(rawIdentity)) + if err != nil { + return []byte{}, []byte{}, err + } + + pub, err := identity.GetPublicKey() + if err != nil { + return []byte{}, []byte{}, err + } + + priv, err := identity.Serialize() + if err != nil { + return []byte{}, []byte{}, err + } + + return priv, pub, nil + default: + return []byte{}, []byte{}, config.ErrKeygenForFormatUnsupported + } +} + +func generateSignatureKey( + signatureFormat string, + password string, +) (privkey []byte, pubkey []byte, err error) { + switch signatureFormat { + case config.SignatureFormatMinisignKey: + pub, rawPriv, err := minisign.GenerateKey(rand.Reader) + if err != nil { + return []byte{}, []byte{}, err + } + + priv, err := minisign.EncryptKey(password, rawPriv) + if err != nil { + return []byte{}, []byte{}, err + } + + return priv, []byte(pub.String()), err + case config.SignatureFormatPGPKey: + return generateEncryptionKey(signatureFormat, password) + default: + return []byte{}, []byte{}, config.ErrKeygenForFormatUnsupported + } +}