Skip to content

Instantly share code, notes, and snippets.

@aditya-r-m
Last active June 26, 2024 07:14
Show Gist options
  • Save aditya-r-m/9e41cec4cd4629f6e7335164a2d8ae71 to your computer and use it in GitHub Desktop.
Save aditya-r-m/9e41cec4cd4629f6e7335164a2d8ae71 to your computer and use it in GitHub Desktop.
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
"os"
"regexp"
"slices"
"strings"
"time"
"path/filepath"
)
func main() {
fmt.Print("\n\033[1;34mPasskey : \033[0m")
var keyString string
fmt.Scan(&keyString)
fmt.Println()
h := sha256.New()
h.Write([]byte(keyString))
key := h.Sum(nil)
entries, err := filepath.Glob("/root/data/*")
if err != nil { panic(err) }
slices.Sort(entries)
plainText := ""
if len(entries) > 0 {
previousFile := entries[len(entries) - 1]
cipherText, err := os.ReadFile(previousFile)
if err != nil { panic(err) }
plainText, err = DecryptMessage(key, cipherText)
if err != nil { panic(err) }
}
currentFile := "/root/data/" + time.Now().Format("06-01-02");
for {
fmt.Print("\033[1;34mCommand : \033[0m")
var command string
fmt.Scan(&command)
switch command {
case "get":
var arg string
fmt.Scan(&arg)
fmt.Println(strings.Join(regexp.MustCompile(arg + `(.*)?\n`).FindAllString(plainText, -1), ""))
case "set":
var argk string
fmt.Scan(&argk)
var argv string
fmt.Scan(&argv)
re := regexp.MustCompile(argk + ` (.*)?\n`)
if re.MatchString(plainText) {
plainText = re.ReplaceAllString(plainText, argk + " " + argv + "\n")
} else {
plainText += argk + " " + argv + "\n"
}
lines := strings.Split(plainText, "\n")
lines = lines[:len(lines) - 1]
slices.Sort(lines)
plainText = strings.Join(lines, "\n") + "\n"
fmt.Println()
case "del":
var arg string
fmt.Scan(&arg)
re := regexp.MustCompile(arg + ` (.*)?\n`)
plainText = re.ReplaceAllString(plainText, "")
fmt.Println()
case "show":
fmt.Println(plainText)
case "list":
re := regexp.MustCompile(` (.*)?\n`)
fmt.Println(re.ReplaceAllString(plainText, "\n"))
case "save":
cipherText, err := EncryptMessage(key, plainText)
if err != nil { panic(err) }
err = os.WriteFile(currentFile, cipherText, 0644)
if err != nil { panic(err) }
fmt.Println()
case "quit":
fmt.Println()
return
default:
fmt.Println("Invalid Command\n")
}
}
}
func EncryptMessage(key []byte, message string) ([]byte, error) {
byteMsg := []byte(message)
block, err := aes.NewCipher(key)
if err != nil { panic(err) }
cipherText := make([]byte, aes.BlockSize+len(byteMsg))
iv := cipherText[:aes.BlockSize]
_, err = io.ReadFull(rand.Reader, iv)
if err != nil { panic(err) }
// https://pkg.go.dev/crypto/cipher#NewCFBEncrypter
stream := cipher.NewCFBEncrypter(block, iv)
stream.XORKeyStream(cipherText[aes.BlockSize:], byteMsg)
return cipherText, nil
}
func DecryptMessage(key []byte, cipherText []byte) (string, error) {
block, err := aes.NewCipher(key)
if err != nil { panic(err) }
if len(cipherText) < aes.BlockSize {
return "", fmt.Errorf("invalid ciphertext block size")
}
iv := cipherText[:aes.BlockSize]
cipherText = cipherText[aes.BlockSize:]
// https://pkg.go.dev/crypto/cipher#NewCFBDecrypter
stream := cipher.NewCFBDecrypter(block, iv)
stream.XORKeyStream(cipherText, cipherText)
return string(cipherText), nil
}

The attached Go script is a minimal secret management utility to securely note personal data such as usernames & passwords.

The application provides read-write interface to work with space-separated key value lines using commands get <key-prefix>, set <key> <value>, del <key>, show, list, save, quit.

The application requires a central key on startup, & loads any previously saved data. The plain text & central key stay in process memory, and only AES-256 encrypted cipher text is written to storage. The [latest] cipher text can be replicated across devices or uploaded to personal cloud drive.

git clone https://gist.github.com/9e41cec4cd4629f6e7335164a2d8ae71.git crypt && cd crypt
docker run --name crypt -itd -v ./crypt.go:/root/crypt.go -v ./data:/root/data golang
docker exec -it crypt go run /root/crypt.go
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment