URI: 
       tsafe.go - safe-go - Unnamed repository; edit this file 'description' to name the repository.
  HTML git clone git://git.z3bra.org/safe-go.git
   DIR Log
   DIR Files
   DIR Refs
       ---
       tsafe.go (3872B)
       ---
            1 package main
            2 
            3 import (
            4         "crypto/rand"
            5         "flag"
            6         "fmt"
            7         "io"
            8         "log"
            9         "os"
           10 
           11         "github.com/netfoundry/secretstream"
           12         "golang.org/x/crypto/argon2"
           13         "golang.org/x/term"
           14 )
           15 
           16 const (
           17         BUFSIZ = 8192
           18 
           19         // default key derivation values in libsodium
           20         argon2id_time_cost        = 2
           21         argon2id_memory_cost      = 67108864 / 1024
           22         argon2id_threads          = 1
           23         argon2id_salt_len         = 16
           24         xchacha20poly1305_key_len = 32
           25         xchacha20poly1305_iv_len  = 16
           26 )
           27 
           28 func usage() {
           29         fmt.Printf("usage: %s [-hed] [-s salt] [-f file]\n", os.Args[0])
           30         os.Exit(2)
           31 }
           32 
           33 func readsalt(f *os.File, salt *[]byte) {
           34         _, err := f.Read(*salt)
           35         if err != nil {
           36                 log.Fatal(err)
           37         }
           38 }
           39 
           40 func deriv(pw []byte, key *[]byte, salt []byte) {
           41         *key = argon2.IDKey(pw, salt,
           42                 argon2id_time_cost,
           43                 argon2id_memory_cost,
           44                 argon2id_threads,
           45                 xchacha20poly1305_key_len)
           46 }
           47 
           48 func encrypt(in *os.File, out *os.File, key []byte, salt []byte) {
           49         var tag byte
           50         buf := make([]byte, BUFSIZ)
           51 
           52         enc, nonce, err := secretstream.NewEncryptor(key)
           53         if err != nil {
           54                 log.Fatal(err)
           55         }
           56 
           57         out.Write(salt)
           58         if err != nil {
           59                 log.Fatal(err)
           60         }
           61 
           62         out.Write(nonce)
           63         if err != nil {
           64                 log.Fatal(err)
           65         }
           66 
           67         loop := 1
           68         tag = secretstream.TagMessage
           69         for loop > 0 {
           70                 n, err := in.Read(buf)
           71                 if err == io.EOF || n < len(buf) {
           72                         tag = secretstream.TagFinal
           73                         loop = 0
           74                 } else if err != nil {
           75                         log.Fatal(err)
           76                 }
           77                 cipher, err := enc.Push(buf[:n], tag)
           78                 if err != nil {
           79                         log.Fatal(err)
           80                 }
           81 
           82                 out.Write(cipher)
           83                 if err != nil {
           84                         log.Fatal(err)
           85                 }
           86         }
           87 }
           88 
           89 func decrypt(in *os.File, out *os.File, key []byte) {
           90         buf := make([]byte, BUFSIZ+secretstream.StreamABytes)
           91         header := make([]byte, secretstream.StreamHeaderBytes)
           92 
           93                 // Skip beginning of file which (supposedly) contains the salt for the key
           94                 in.Seek(argon2id_salt_len, os.SEEK_SET)
           95         _, err := in.Read(header)
           96         if err != nil {
           97                 log.Fatal(err)
           98         }
           99 
          100         dec, err := secretstream.NewDecryptor(key, header)
          101         if err != nil {
          102                 log.Fatal(err)
          103         }
          104 
          105         loop := 1
          106         for loop > 0 {
          107                 n, err := in.Read(buf)
          108                 if err != nil && err != io.EOF {
          109                         log.Fatal(err)
          110                 }
          111 
          112                 plain, tag, err := dec.Pull(buf[:n])
          113                 if err != nil {
          114                         log.Fatal(err)
          115                 }
          116 
          117                 if tag == secretstream.TagFinal {
          118                         loop = 0
          119                 }
          120                 out.Write(plain)
          121         }
          122 }
          123 
          124 func main() {
          125         var err error
          126         var key, salt, pass []byte
          127         var filename, saltfile string
          128         var dflag, eflag bool
          129 
          130         in := os.Stdin
          131         out := os.Stdout
          132 
          133         salt = make([]byte, argon2id_salt_len)
          134         key = make([]byte, xchacha20poly1305_key_len)
          135 
          136         flag.StringVar(&filename, "f", "", "Encrypt/decrypt to/from file name")
          137         flag.StringVar(&saltfile, "s", "", "Read salt from file (encrypt-only)")
          138         flag.BoolVar(&eflag, "e", false, "encrypt input (default)")
          139         flag.BoolVar(&dflag, "d", false, "decrypt input")
          140         flag.Usage = usage
          141         flag.Parse()
          142 
          143         if eflag && dflag {
          144                 log.Fatal("Cannot use encryption and decryption at the same time")
          145         }
          146 
          147         args := flag.Args()
          148         if len(args) > 0 {
          149                 usage()
          150         }
          151 
          152         // Prompt user for password
          153         tty, err := os.OpenFile("/dev/tty", os.O_RDWR, 0644)
          154         if err != nil {
          155                 log.Fatal(err)
          156         }
          157         defer tty.Close()
          158         fmt.Fprint(tty, "password:")
          159         pass, _ = term.ReadPassword(int(tty.Fd()))
          160         fmt.Fprintln(tty, "")
          161 
          162         if dflag {
          163                 if len(filename) > 0 {
          164                         in, err = os.Open(filename)
          165                         if err != nil {
          166                                 log.Fatal(err)
          167                         }
          168                         defer in.Close()
          169                 }
          170 
          171                 readsalt(in, &salt)
          172                 deriv(pass, &key, salt)
          173                 decrypt(in, out, key)
          174         } else {
          175                 if len(saltfile) > 0 {
          176                         // Read salt from any manually specified file…
          177                         f, err := os.Open(saltfile)
          178                         if err != nil {
          179                                 log.Fatal(err)
          180                         }
          181                         readsalt(f, &salt)
          182                         f.Close()
          183                 } else {
          184                         // … or generate a random one
          185                         _, err = rand.Read(salt)
          186                         if err != nil {
          187                                 log.Fatal(err)
          188                         }
          189                 }
          190                 deriv(pass, &key, salt)
          191                 if len(filename) > 0 {
          192                         out, err := os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
          193                         if err != nil {
          194                                 log.Fatal(err)
          195                         }
          196                         defer out.Close()
          197                 }
          198                 encrypt(in, out, key, salt)
          199         }
          200 }