Initial migration from GPG to RCW

This commit is contained in:
2025-05-04 17:41:44 -04:00
parent a95bff8ef4
commit 649b60e2fe
17 changed files with 51 additions and 123 deletions
-6
View File
@@ -12,9 +12,3 @@ const (
PathSeparator = "/" // Platform-specific path separator
IsWindows = false // Platform indicator
)
// enableVirtualTerminalProcessing is a dummy function on UNIX-like systems (only needed on Windows).
// TODO Remove after migration off of GPG, as pinentry is responsible for disabling ANSI escape sequence interpretation.
func enableVirtualTerminalProcessing() {
return
}
-17
View File
@@ -2,11 +2,6 @@
package core
import (
"os"
"syscall"
)
var (
EntryRoot = Home + "\\AppData\\Local\\libmutton\\entries" // Path to libmutton entry directory
ConfigDir = Home + "\\AppData\\Local\\libmutton\\config" // Path to libmutton configuration directory
@@ -17,15 +12,3 @@ const (
PathSeparator = "\\" // Platform-specific path separator
IsWindows = true // Platform indicator
)
// enableVirtualTerminalProcessing ensures ANSI escape sequences are interpreted properly on Windows.
// TODO Remove after migration off of GPG, as pinentry is responsible for disabling ANSI escape sequence interpretation.
func enableVirtualTerminalProcessing() {
stdout := syscall.Handle(os.Stdout.Fd())
var originalMode uint32
syscall.GetConsoleMode(stdout, &originalMode)
originalMode |= 0x0004
syscall.MustLoadDLL("kernel32").MustFindProc("SetConsoleMode").Call(uintptr(stdout), uintptr(originalMode))
}
+2 -2
View File
@@ -12,10 +12,10 @@ import (
)
// CopyArgument copies a field from an entry to the clipboard.
func CopyArgument(targetLocation string, field int) {
func CopyArgument(targetLocation string, field int, passphrase []byte) {
if isFile, _ := TargetIsFile(targetLocation, true, 2); isFile {
decryptedEntry := DecryptGPG(targetLocation)
decryptedEntry := DecryptFileToSlice(targetLocation, passphrase)
var copySubject string // will store data to be copied
// ensure field exists in entry
+2 -2
View File
@@ -9,7 +9,7 @@ import (
// copyString copies a string to the clipboard.
func copyString(continuous bool, copySubject string) {
cmd := exec.Command("pbcopy")
WriteToStdin(cmd, copySubject)
writeToStdin(cmd, copySubject)
err := cmd.Run()
if err != nil {
PrintError("Failed to copy to clipboard: "+err.Error(), ErrorClipboard, true)
@@ -23,6 +23,6 @@ func copyString(continuous bool, copySubject string) {
// getClipCommands returns the commands for pasting and clearing the clipboard contents.
func getClipCommands() (*exec.Cmd, *exec.Cmd) {
cmdClear := exec.Command("pbcopy")
WriteToStdin(cmdClear, "")
writeToStdin(cmdClear, "")
return exec.Command("pbpaste"), cmdClear
}
+2 -2
View File
@@ -9,7 +9,7 @@ import (
// copyString copies a string to the clipboard.
func copyString(continuous bool, copySubject string) {
cmd := exec.Command("termux-clipboard-set")
WriteToStdin(cmd, copySubject)
writeToStdin(cmd, copySubject)
err := cmd.Run()
if err != nil {
PrintError("Failed to copy to clipboard: "+err.Error(), ErrorClipboard, true)
@@ -23,6 +23,6 @@ func copyString(continuous bool, copySubject string) {
// getClipCommands returns the commands for pasting and clearing the clipboard contents.
func getClipCommands() (*exec.Cmd, *exec.Cmd) {
cmdClear := exec.Command("termux-clipboard-set")
WriteToStdin(cmdClear, "")
writeToStdin(cmdClear, "")
return exec.Command("termux-clipboard-get"), cmdClear
}
+1 -1
View File
@@ -21,7 +21,7 @@ func copyString(continuous bool, copySubject string) {
PrintError("Clipboard platform could not be determined", ErrorClipboard, true)
}
WriteToStdin(cmdCopy, copySubject)
writeToStdin(cmdCopy, copySubject)
err := cmdCopy.Run()
if err != nil {
PrintError("Failed to copy to clipboard: "+err.Error(), ErrorClipboard, true)
+2 -2
View File
@@ -1,12 +1,12 @@
package core
// GetOldEntryData decrypts and returns old entry data (with all required lines present).
func GetOldEntryData(targetLocation string, field int) []string {
func GetOldEntryData(targetLocation string, field int, passphrase []byte) []string {
// ensure targetLocation exists
TargetIsFile(targetLocation, true, 2)
// read old entry data
unencryptedEntry := DecryptGPG(targetLocation)
unencryptedEntry := DecryptFileToSlice(targetLocation, passphrase)
// return the old entry data with all required lines present
if field > 0 {
-35
View File
@@ -1,35 +0,0 @@
package core
import (
"os/exec"
"strings"
)
// TODO GPG support is a temporary feature - It will be replaced with a different encryption scheme in the future
// DecryptGPG decrypts a GPG-encrypted file and returns the contents as a slice of (trimmed) strings.
func DecryptGPG(targetLocation string) []string {
cmd := exec.Command("gpg", "--pinentry-mode", "loopback", "-q", "-d", targetLocation)
output, err := cmd.Output()
// ensure ANSI escape sequences are interpreted properly on Windows
enableVirtualTerminalProcessing()
if err != nil {
PrintError("Failed to decrypt \""+targetLocation+"\" - Ensure it is a valid GPG-encrypted file and that you entered your passphrase correctly", ErrorDecryption, true)
}
return strings.Split(string(output), "\n")
}
// EncryptGPG encrypts a slice of strings using GPG and returns the encrypted data as a byte slice.
func EncryptGPG(input []string) []byte {
gpgCfg, _ := ParseConfig([][2]string{{"LIBMUTTON", "gpgID"}}, "")
cmd := exec.Command("gpg", "-q", "-r", gpgCfg[0], "-e")
WriteToStdin(cmd, strings.Join(input, "\n"))
encryptedBytes, err := cmd.Output()
if err != nil {
PrintError("Failed to encrypt data - Ensure that you have a valid GPG ID set in libmutton.ini", ErrorEncryption, true)
}
return encryptedBytes
}
+6 -42
View File
@@ -2,52 +2,16 @@ package core
import (
"os"
"os/exec"
"strconv"
"strings"
"time"
"github.com/rwinkhart/rcw/wrappers"
)
// GpgUIDListGen generates a list of all GPG key IDs on the system and returns them as a slice of strings.
func GpgUIDListGen() []string {
cmd := exec.Command("gpg", "-k", "--with-colons")
gpgOutputBytes, _ := cmd.Output()
gpgOutputLines := strings.Split(string(gpgOutputBytes), "\n")
var uidSlice []string
for _, line := range gpgOutputLines {
if strings.HasPrefix(line, "uid") {
uid := strings.Split(line, ":")[9]
uidSlice = append(uidSlice, uid)
}
}
return uidSlice
}
// GpgKeyGen generates a new GPG key and returns the key ID.
func GpgKeyGen() string {
gpgGenTempFile := CreateTempFile()
defer func(name string) {
_ = os.Remove(name) // error ignored; if the file could be created, it can probably be removed
}(gpgGenTempFile.Name())
// create and write gpg-gen file
unixTime := strconv.FormatInt(time.Now().Unix(), 10)
_, _ = gpgGenTempFile.WriteString(strings.Join([]string{"Key-Type: eddsa", "Key-Curve: ed25519", "Key-Usage: sign", "Subkey-Type: ecdh", "Subkey-Curve: cv25519", "Subkey-Usage: encrypt", "Name-Real: libmutton-" + unixTime, "Name-Comment: gpg-libmutton", "Name-Email: github.com/rwinkhart/libmutton", "Expire-Date: 0"}, "\n")) // error ignored; if the file could be created, it can probably be written to
// close gpg-gen file
_ = gpgGenTempFile.Close() // error ignored; if the file could be created, it can probably be closed
// generate GPG key based on gpg-gen file
cmd := exec.Command("gpg", "-q", "--batch", "--generate-key", gpgGenTempFile.Name())
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
cmd.Stdin = os.Stdin
err := cmd.Run()
// RCWSanityCheckGen generates the RCW sanity check file for libmutton.
func RCWSanityCheckGen(passphrase []byte) {
err := wrappers.GenSanityCheck(ConfigDir+"/sanity.rcw", passphrase)
if err != nil {
PrintError("Failed to generate GPG key: "+err.Error(), ErrorOther, true)
PrintError("Failed to generate sanity check file: "+err.Error(), ErrorWrite, true)
}
return "libmutton-" + unixTime + " (gpg-libmutton) <github.com/rwinkhart/libmutton>"
}
// DirInit creates the libmutton directories.
+1 -1
View File
@@ -12,7 +12,7 @@ import (
func LaunchClipClearProcess(copySubject string) {
executableName := os.Args[0]
cmd := exec.Command(executableName, "clipclear")
WriteToStdin(cmd, copySubject)
writeToStdin(cmd, copySubject)
err := cmd.Start()
if err != nil {
PrintError("Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?", ErrorClipboard, true)
+1 -1
View File
@@ -13,7 +13,7 @@ import (
func LaunchClipClearProcess(copySubject string, isWayland bool) {
executableName := os.Args[0]
cmd := exec.Command(executableName, "clipclear", strconv.FormatBool(isWayland))
WriteToStdin(cmd, copySubject)
writeToStdin(cmd, copySubject)
err := cmd.Start()
if err != nil {
PrintError("Failed to launch automated clipboard clearing process - Does this libmutton implementation support the \"clipclear\" argument?", ErrorClipboard, true)
+27
View File
@@ -0,0 +1,27 @@
package core
import (
"os"
"strings"
"github.com/rwinkhart/rcw/wrappers"
)
// DecryptFileToSlice decrypts an RCW wrapped file and returns the contents as a slice of (trimmed) strings.
func DecryptFileToSlice(targetLocation string, passphrase []byte) []string {
encBytes, err := os.ReadFile(targetLocation)
if err != nil {
PrintError("Failed to decrypt \""+targetLocation+"\" - "+err.Error(), ErrorDecryption, true)
}
decBytes, err := wrappers.Decrypt(encBytes, passphrase)
if err != nil {
PrintError("Failed to decrypt \""+targetLocation+"\" - "+err.Error(), ErrorDecryption, true)
}
return strings.Split(string(decBytes), "\n")
}
// EncryptBytes encrypts a byte slice using RCW and returns the encrypted data.
func EncryptBytes(decBytes []byte, passphrase []byte) []byte {
encBytes := wrappers.Encrypt(decBytes, passphrase)
return encBytes
}
+4 -5
View File
@@ -36,17 +36,16 @@ func TargetIsFile(targetLocation string, errorOnFail bool, failCondition uint8)
}
// WriteEntry writes entryData to an encrypted file at targetLocation.
func WriteEntry(targetLocation string, entryData []string) {
encryptedBytes := EncryptGPG(entryData)
func WriteEntry(targetLocation string, entryData []byte, passphrase []byte) {
encryptedBytes := EncryptBytes(entryData, passphrase)
err := os.WriteFile(targetLocation, encryptedBytes, 0600)
if err != nil {
PrintError("Failed to write to file: "+err.Error(), ErrorWrite, true)
}
}
// WriteToStdin is a utility function that writes a string to a command's stdin.
// TODO unexport (import?) after migration off of GPG
func WriteToStdin(cmd *exec.Cmd, input string) {
// writeToStdin is a utility function that writes a string to a command's stdin.
func writeToStdin(cmd *exec.Cmd, input string) {
stdin, err := cmd.StdinPipe()
if err != nil {
PrintError("Failed to access stdin for system command: "+err.Error(), ErrorOther, true)