tFirst commit - cream - Stream encryption utility
(HTM) git clone git://git.z3bra.org/cream.git
(DIR) Log
(DIR) Files
(DIR) Refs
---
(DIR) commit 7316d185de6c7e7c8602d2fb6e073e4c531e0a11
(HTM) Author: Willy Goiffon <dev@z3bra.org>
Date: Wed, 14 Sep 2022 17:41:59 +0200
First commit
Diffstat:
A config.mk | 5 +++++
A cream.go | 200 +++++++++++++++++++++++++++++++
A go.mod | 11 +++++++++++
A go.sum | 14 ++++++++++++++
A makefile | 19 +++++++++++++++++++
A mkfile | 17 +++++++++++++++++
6 files changed, 266 insertions(+), 0 deletions(-)
---
(DIR) diff --git a/config.mk b/config.mk
t@@ -0,0 +1,5 @@
+GO = go
+GOOS = `{uname -s | tr A-Z a-z}
+
+PREFIX = /usr/local
+MANDIR = ${PREFIX}/man
(DIR) diff --git a/cream.go b/cream.go
t@@ -0,0 +1,200 @@
+package main
+
+import (
+ "crypto/rand"
+ "flag"
+ "fmt"
+ "io"
+ "log"
+ "os"
+
+ "github.com/netfoundry/secretstream"
+ "golang.org/x/crypto/argon2"
+ "golang.org/x/term"
+)
+
+const (
+ BUFSIZ = 8192
+
+ // default key derivation values in libsodium
+ argon2id_time_cost = 2
+ argon2id_memory_cost = 67108864 / 1024
+ argon2id_threads = 1
+ argon2id_salt_len = 16
+ xchacha20poly1305_key_len = 32
+ xchacha20poly1305_iv_len = 16
+)
+
+func usage() {
+ fmt.Printf("usage: %s [-hed] [-s salt] [-f file]\n", os.Args[0])
+ os.Exit(2)
+}
+
+func readsalt(f *os.File, salt *[]byte) {
+ _, err := f.Read(*salt)
+ if err != nil {
+ log.Fatal(err)
+ }
+}
+
+func deriv(pw []byte, key *[]byte, salt []byte) {
+ *key = argon2.IDKey(pw, salt,
+ argon2id_time_cost,
+ argon2id_memory_cost,
+ argon2id_threads,
+ xchacha20poly1305_key_len)
+}
+
+func encrypt(in *os.File, out *os.File, key []byte, salt []byte) {
+ var tag byte
+ buf := make([]byte, BUFSIZ)
+
+ enc, nonce, err := secretstream.NewEncryptor(key)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ out.Write(salt)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ out.Write(nonce)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ loop := 1
+ tag = secretstream.TagMessage
+ for loop > 0 {
+ n, err := in.Read(buf)
+ if err == io.EOF || n < len(buf) {
+ tag = secretstream.TagFinal
+ loop = 0
+ } else if err != nil {
+ log.Fatal(err)
+ }
+ cipher, err := enc.Push(buf[:n], tag)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ out.Write(cipher)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+}
+
+func decrypt(in *os.File, out *os.File, key []byte) {
+ buf := make([]byte, BUFSIZ+secretstream.StreamABytes)
+ header := make([]byte, secretstream.StreamHeaderBytes)
+
+ // Skip beginning of file which (supposedly) contains the salt for the key
+ in.Seek(argon2id_salt_len, os.SEEK_SET)
+ _, err := in.Read(header)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ dec, err := secretstream.NewDecryptor(key, header)
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ loop := 1
+ for loop > 0 {
+ n, err := in.Read(buf)
+ if err != nil && err != io.EOF {
+ log.Fatal(err)
+ }
+
+ plain, tag, err := dec.Pull(buf[:n])
+ if err != nil {
+ log.Fatal(err)
+ }
+
+ if tag == secretstream.TagFinal {
+ loop = 0
+ }
+ out.Write(plain)
+ }
+}
+
+func main() {
+ var err error
+ var key, salt, pass []byte
+ var filename, saltfile string
+ var dflag, eflag bool
+
+ in := os.Stdin
+ out := os.Stdout
+
+ salt = make([]byte, argon2id_salt_len)
+ key = make([]byte, xchacha20poly1305_key_len)
+
+ flag.StringVar(&filename, "f", "", "Encrypt/decrypt to/from file name")
+ flag.StringVar(&saltfile, "s", "", "Read salt from file (encrypt-only)")
+ flag.BoolVar(&eflag, "e", false, "encrypt input (default)")
+ flag.BoolVar(&dflag, "d", false, "decrypt input")
+ flag.Usage = usage
+ flag.Parse()
+
+ if eflag && dflag {
+ log.Fatal("Cannot use encryption and decryption at the same time")
+ }
+
+ args := flag.Args()
+ if len(args) > 0 {
+ usage()
+ }
+
+ // Prompt user for password
+ tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0644)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer tty.Close()
+ fmt.Fprint(tty, "password:")
+ pass, _ = term.ReadPassword(int(tty.Fd()))
+ fmt.Fprintln(tty, "")
+
+ if dflag {
+ if len(filename) > 0 {
+ in, err = os.Open(filename)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer in.Close()
+ }
+
+ readsalt(in, &salt)
+ deriv(pass, &key, salt)
+ decrypt(in, out, key)
+ } else {
+ if len(saltfile) > 0 {
+ // Read salt from any manually specified file…
+ f, err := os.Open(saltfile)
+ if err != nil {
+ log.Fatal(err)
+ }
+ readsalt(f, &salt)
+ f.Close()
+ } else {
+ // … or generate a random one
+ _, err = rand.Read(salt)
+ if err != nil {
+ log.Fatal(err)
+ }
+ }
+ deriv(pass, &key, salt)
+ if len(filename) > 0 {
+ out, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
+ if err != nil {
+ log.Fatal(err)
+ }
+ defer out.Close()
+ }
+ encrypt(in, out, key, salt)
+ }
+}
(DIR) diff --git a/go.mod b/go.mod
t@@ -0,0 +1,11 @@
+module z3bra.org/safe
+
+go 1.19
+
+require (
+ github.com/netfoundry/secretstream v0.1.2
+ golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90
+ golang.org/x/term v0.0.0-20210927222741-03fcf44c2211
+)
+
+require golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
(DIR) diff --git a/go.sum b/go.sum
t@@ -0,0 +1,14 @@
+github.com/netfoundry/secretstream v0.1.2 h1:NgqrYytDnjKbOfWI29TT0SJM+RwB3yf9MIkJVJaU+J0=
+github.com/netfoundry/secretstream v0.1.2/go.mod h1:uasYkYSp0MmNSlKOWJ2sVzxPms8e58TS4ENq4yro86k=
+golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
+golang.org/x/crypto v0.0.0-20200204104054-c9f3fb736b72/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
+golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM=
+golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
+golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
+golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
+golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
+golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
(DIR) diff --git a/makefile b/makefile
t@@ -0,0 +1,19 @@
+-include config.mk
+
+.SUFFIXES: .go
+
+all: cream
+
+.go:
+ ${GO} build -o $@ $<
+
+clean:
+ rm -f cream
+
+install: cream
+ mkdir -p ${DESTDIR}${PREFIX}/bin
+ cp cream ${DESTDIR}${PREFIX}/bin/cream
+ chmod 755 ${DESTDIR}${PREFIX}/bin/cream
+
+uninstall:
+ rm ${DESTDIR}${PREFIX}/bin/cream
(DIR) diff --git a/mkfile b/mkfile
t@@ -0,0 +1,17 @@
+<config.mk
+
+all:V: cream
+
+%: %.go
+ $GO build -o $stem $stem.go
+
+clean:V:
+ rm -f cream
+
+install:V: cream
+ mkdir -p ${DESTDIR}${PREFIX}/bin
+ cp cream ${DESTDIR}${PREFIX}/bin/cream
+ chmod 755 ${DESTDIR}${PREFIX}/bin/cream
+
+uninstall:V:
+ rm ${DESTDIR}${PREFIX}/bin/cream