Port to latest libmutton development version & begin migration from GPG to RCW

This commit is contained in:
2025-05-04 18:07:02 -04:00
parent b366d1e13e
commit a144b2769d
9 changed files with 56 additions and 44 deletions
+1 -1
View File
@@ -11,7 +11,7 @@ var (
)
const (
MUTNVersion = "0.2.6" // untagged releases feature a letter suffix corresponding to the eventual release version, e.g "0.2.A" -> "0.2.0", "0.2.B" -> "0.2.1"
MUTNVersion = "0.D.0" // untagged releases feature a letter suffix corresponding to the eventual release version, e.g "0.2.A" -> "0.2.0", "0.2.B" -> "0.2.1"
AnsiBold = "\033[1m"
ansiBlackOnWhite = "\033[38;5;0;48;5;15m"
+4 -4
View File
@@ -22,16 +22,16 @@ func RenameCli(oldLocationIncomplete string) {
// EditEntryField edits a field of an entry at targetLocation (user input).
func EditEntryField(targetLocation string, hideSecrets bool, field int) {
// fetch old entry data (with all required lines present)
unencryptedEntry := core.GetOldEntryData(targetLocation, field)
unencryptedEntry := core.GetOldEntryData(targetLocation, field, GetRCWPassphrase())
// edit the field
switch field {
case 0:
unencryptedEntry[field] = string(inputHidden("Password:"))
unencryptedEntry[field] = string(GetRCWPassphrase())
case 1:
unencryptedEntry[field] = input("Username:")
case 2:
unencryptedEntry[field] = string(inputHidden("TOTP secret:"))
unencryptedEntry[field] = string(GetRCWPassphrase())
case 3:
unencryptedEntry[field] = input("URL:")
case 4: // edit notes fields
@@ -54,7 +54,7 @@ func EditEntryField(targetLocation string, hideSecrets bool, field int) {
// GenUpdate generates a new password for an entry at targetLocation (user input).
func GenUpdate(targetLocation string, hideSecrets bool) {
// fetch old entry data
unencryptedEntry := core.GetOldEntryData(targetLocation, 0)
unencryptedEntry := core.GetOldEntryData(targetLocation, 0, GetRCWPassphrase())
// generate a new password
unencryptedEntry[0] = inputPasswordGen()
+2 -2
View File
@@ -68,10 +68,10 @@ func EntryReader(decryptedEntry []string, hideSecrets, syncEnabled bool) {
os.Exit(0)
}
// EntryReaderDecrypt is a wrapper for EntryReader that first decrypts a GPG-encrypted file before sending it to EntryReader.
// EntryReaderDecrypt is a wrapper for EntryReader that first decrypts an RCW-wrapped file before sending it to EntryReader.
func EntryReaderDecrypt(targetLocation string, hideSecrets bool) {
if isFile, _ := core.TargetIsFile(targetLocation, true, 2); isFile {
EntryReader(core.DecryptGPG(targetLocation), hideSecrets, false) // never sync if decrypting straight to EntryReader, as this means the entry could not have been modified
EntryReader(core.DecryptFileToSlice(targetLocation, GetRCWPassphrase()), hideSecrets, false) // never sync if decrypting straight to EntryReader, as this means the entry could not have been modified
}
// do not exit, as this is the job of EntryReader
}
+16 -17
View File
@@ -1,6 +1,7 @@
package cli
import (
"bytes"
"cmp"
"fmt"
"os"
@@ -12,21 +13,7 @@ import (
// TempInitCli initializes the MUTN environment based on user input.
func TempInitCli() {
// gpgID
var gpgID string
if inputBinary("Auto-generate GPG key?") {
gpgID = core.GpgKeyGen()
} else {
// select GPG key from menu
uidSlice := core.GpgUIDListGen()
gpgIDInt := inputMenuGen("Select GPG key:", uidSlice)
if gpgIDInt == 0 {
core.PrintError("No GPG keys found - please generate one", core.ErrorTargetNotFound, true)
}
gpgID = uidSlice[gpgIDInt-1]
}
// textEditor
// text editor
textEditor := cmp.Or(input("Text editor (leave blank for $EDITOR, falls back to \""+fallbackEditor+"\"):"), os.Getenv("EDITOR"), fallbackEditor)
// SSH info
@@ -56,7 +43,7 @@ func TempInitCli() {
oldDeviceID := core.DirInit(false)
// write config file (temporarily assigns sshEntryRoot and sshIsWindows to null to pass initial device ID registration)
core.WriteConfig([][3]string{{"MUTN", "textEditor", textEditor}, {"LIBMUTTON", "gpgID", gpgID}, {"LIBMUTTON", "sshUser", sshUser}, {"LIBMUTTON", "sshIP", sshIP}, {"LIBMUTTON", "sshPort", sshPort}, {"LIBMUTTON", "sshKey", sshKey}, {"LIBMUTTON", "sshKeyProtected", strconv.FormatBool(sshKeyProtected)}, {"LIBMUTTON", "sshEntryRoot", "null"}, {"LIBMUTTON", "sshIsWindows", "false"}}, nil, false)
core.WriteConfig([][3]string{{"MUTN", "textEditor", textEditor}, {"LIBMUTTON", "sshUser", sshUser}, {"LIBMUTTON", "sshIP", sshIP}, {"LIBMUTTON", "sshPort", sshPort}, {"LIBMUTTON", "sshKey", sshKey}, {"LIBMUTTON", "sshKeyProtected", strconv.FormatBool(sshKeyProtected)}, {"LIBMUTTON", "sshEntryRoot", "null"}, {"LIBMUTTON", "sshIsWindows", "false"}}, nil, false)
// generate and register device ID
sshEntryRoot, sshIsWindows := sync.DeviceIDGen(oldDeviceID)
@@ -68,6 +55,18 @@ func TempInitCli() {
core.DirInit(false)
// write config file
core.WriteConfig([][3]string{{"MUTN", "textEditor", textEditor}, {"LIBMUTTON", "gpgID", gpgID}}, nil, false)
core.WriteConfig([][3]string{{"MUTN", "textEditor", textEditor}}, nil, false)
}
// RCW sanity check file
var passphraseInput1 []byte
for {
passphraseInput1 = inputHidden("New master (RCW) passphrase:")
passphraseInput2 := inputHidden("Confirm RCW passphrase:")
if !bytes.Equal(passphraseInput1, passphraseInput2) {
fmt.Println(core.AnsiError + "Passphrases do not match" + core.AnsiReset)
continue
}
core.RCWSanityCheckGen(passphraseInput1)
break
}
}
+17 -1
View File
@@ -8,9 +8,24 @@ import (
"github.com/rwinkhart/libmutton/core"
"github.com/rwinkhart/libmutton/sync"
"github.com/rwinkhart/rcw/wrappers"
"golang.org/x/term"
)
// GetRCWPassphrase retrieves the RCW passphrase from the user.
// TODO attempt to retrieve from RCW daemon
func GetRCWPassphrase() []byte {
for {
passphrase := inputHidden("RCW Passphrase:")
err := wrappers.RunSanityCheck(core.ConfigDir+"/sanity.rcw", passphrase)
if err != nil {
fmt.Println(core.AnsiError + "Failed to encrypt - " + err.Error() + core.AnsiReset)
continue
}
return passphrase
}
}
// input prompts the user for input and returns the input as a string.
func input(prompt string) string {
fmt.Print("\n" + prompt + " ")
@@ -85,7 +100,8 @@ func inputPasswordGen() string {
func writeEntryCLI(targetLocation string, unencryptedEntry []string, hideSecrets bool) {
if core.EntryIsNotEmpty(unencryptedEntry) {
// write the entry to the target location
core.WriteEntry(targetLocation, unencryptedEntry)
joinedUnencryptedEntry := strings.Join(unencryptedEntry, "\n")
core.WriteEntry(targetLocation, []byte(joinedUnencryptedEntry), GetRCWPassphrase())
// preview the entry
fmt.Println(AnsiBold + "\nEntry Preview:" + core.AnsiReset)
EntryReader(unencryptedEntry, hideSecrets, true)