Skip to content

Instantly share code, notes, and snippets.

@aditya-r-m
Last active October 5, 2024 04:12
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"
)
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)
file := "./crypt.log";
plainText := ""
cipherText, err := os.ReadFile(file)
if err == nil {
plainText, err = DecryptMessage(key, cipherText)
if err != nil { panic(err) }
}
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(file, 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 CLI application can be started with a simple go run crypt.go. It 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 cipher text can be replicated across devices or uploaded to personal cloud drive.

The script can also be tweaked slightly to capture encrypted timed logs instead of key-value pairs,

package main

import (
        "bufio"
        "crypto/aes"
        "crypto/cipher"
        "crypto/rand"
        "crypto/sha256"
        "fmt"
        "io"
        "os"
        "strings"
        "time"
)

func main() {
        .
        .
        .
        fmt.Print("\033[1;34mUser : \033[0m")
        var user string
        fmt.Scan(&user)
        fmt.Println()

        rd := bufio.NewReader(os.Stdin)
        for {
                fmt.Print("\033[1;34m> \033[0m")
                var command string
                command, err := rd.ReadString('\n')
                if err != nil { panic(err) }
                
                switch strings.Fields(command)[0] {
                        case ":r":
                                fmt.Println(plainText)
                        case ":w":
                                cipherText, err := EncryptMessage(key, plainText)
                                if err != nil { panic(err) }
                                err = os.WriteFile(file, cipherText, 0644)
                                if err != nil { panic(err) }
                                fmt.Println()
                        case ":q":
                                fmt.Println()
                                return
                        default:
                                plainText += "\n\033[1;35m[" + time.Now().Format("06-01-02_03:04:05PM") + "] " + user + " >\033[0m " + command;
                                fmt.Println()
                }
        }
}
.
.
.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment