thashgen: generate argon2id hashes - hashcrush - Compute Argon2id hashes
 (HTM) git clone git://git.z3bra.org/hashcrush.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
       ---
 (DIR) commit dbd234d5b5411648317f8564c6980d5f5fac5f88
 (DIR) parent d81e11cf5c2fc81f0cb8c8c6724e45fe68612af1
 (HTM) Author: Willy Goiffon <contact@z3bra.org>
       Date:   Tue, 15 Aug 2023 12:15:13 +0200
       
       hashgen: generate argon2id hashes
       
       Diffstat:
         A hashgen/go.mod                      |      10 ++++++++++
         A hashgen/go.sum                      |       6 ++++++
         A hashgen/hashgen.go                  |     110 +++++++++++++++++++++++++++++++
         A hashgen/makefile                    |       5 +++++
       
       4 files changed, 131 insertions(+), 0 deletions(-)
       ---
 (DIR) diff --git a/hashgen/go.mod b/hashgen/go.mod
       t@@ -0,0 +1,10 @@
       +module z3bra.org/hashgen
       +
       +go 1.19
       +
       +require (
       +        golang.org/x/crypto v0.5.0
       +        golang.org/x/term v0.4.0
       +)
       +
       +require golang.org/x/sys v0.4.0 // indirect
 (DIR) diff --git a/hashgen/go.sum b/hashgen/go.sum
       t@@ -0,0 +1,6 @@
       +golang.org/x/crypto v0.5.0 h1:U/0M97KRkSFvyD/3FSmdP5W5swImpNgle/EHFhOsQPE=
       +golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
       +golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
       +golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
       +golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
       +golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
 (DIR) diff --git a/hashgen/hashgen.go b/hashgen/hashgen.go
       t@@ -0,0 +1,110 @@
       +/*
       + * Copyright: this software is in the public domain
       + *
       + * Generate argon2id hashes from a given password
       + * Salt may be provided, or generated randomly
       + * Output format:
       + *
       + *     $argon2<T>[$v=<num>]$m=<num>,t=<num>,p=<num>$<hex>$<hex>
       + *
       + * by wgs
       + */
       +
       +package main
       +
       +import (
       +        "crypto/rand"
       +        "encoding/base64"
       +        "flag"
       +        "fmt"
       +        "log"
       +        "os"
       +
       +        "golang.org/x/crypto/chacha20poly1305"
       +        "golang.org/x/crypto/argon2"
       +        "golang.org/x/term"
       +)
       +
       +const (
       +        // recommended for memory constrained environments, as per RFC9106
       +        argon2id_memory_cost      = 65536 // 64 Kib
       +        argon2id_time_cost        = 3
       +        argon2id_threads          = 4
       +        argon2id_salt_len         = 16
       +)
       +
       +func usage() {
       +        fmt.Printf("usage: %s [-hed] [-b size] [-j thread] [-t time] [-m memory] [-p pass] [-s salt] [-f file]\n", os.Args[0])
       +        os.Exit(2)
       +}
       +
       +func deriv(pw []byte, key *[]byte, time, mem, job uint32, salt []byte) {
       +        klen := len(*key)
       +        *key = argon2.IDKey(pw, salt, time, mem, uint8(job), uint32(klen))
       +}
       +
       +func main() {
       +        var err error
       +        var salt [16]byte
       +        var key, pass []byte
       +        var password, saltfile string
       +
       +        var time uint64
       +        var memory uint64
       +        var jobnum uint64
       +
       +        key = make([]byte, chacha20poly1305.KeySize)
       +
       +        flag.StringVar(&password, "p", "", "Password")
       +        flag.StringVar(&saltfile, "s", "", "Read salt from file")
       +
       +        // argon2id parameters
       +        flag.Uint64Var(&memory, "m", argon2id_memory_cost, "Memory cost")
       +        flag.Uint64Var(&time,   "t", argon2id_time_cost, "Time cost")
       +        flag.Uint64Var(&jobnum, "j", argon2id_threads, "Parallel threads")
       +
       +        flag.Usage = usage
       +        flag.Parse()
       +
       +        args := flag.Args()
       +        if len(args) > 0 {
       +                usage()
       +        }
       +
       +        pass = []byte(password)
       +
       +        // Prompt user for password if provided password is empty
       +        if len(pass) == 0 {
       +                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 len(saltfile) > 0 {
       +                // Read salt from any manually specified file…
       +                f, err := os.Open(saltfile)
       +                if err != nil {
       +                        log.Fatal(err)
       +                }
       +                _, err = f.Read(salt[:])
       +                f.Close()
       +        } else {
       +                // … or generate a random one
       +                _, err = rand.Read(salt[:])
       +                if err != nil {
       +                        log.Fatal(err)
       +                }
       +        }
       +
       +        deriv(pass, &key, uint32(time), uint32(memory), uint32(jobnum), salt[:])
       +
       +        b64_s := base64.StdEncoding.WithPadding(base64.NoPadding).EncodeToString(salt[:])
       +        b64_k := base64.StdEncoding.WithPadding(base64.NoPadding).EncodeToString(key[:])
       +
       +        fmt.Printf("$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s\n", argon2.Version, memory, time, jobnum, b64_s, b64_k)
       +}
 (DIR) diff --git a/hashgen/makefile b/hashgen/makefile
       t@@ -0,0 +1,5 @@
       +hashgen: hashgen.go
       +        go build
       +
       +clean:
       +        rm -f hashgen