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