165
package main
import (
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"git.sophuwu.com/gophuwu/flags"
"git.sophuwu.com/gophuwu/parsers"
"golang.org/x/term"
"hash"
"os"
)
func fatal(err error, msg string) {
if err != nil {
println("fatal: " + msg + ": " + err.Error())
os.Exit(1)
}
}
func init() {
err := flags.NewFlag("algorithm", "a", "hash with sha1, sha256 or sha512", "sha256")
fatal(err, "could not add algorithm flag")
err = flags.NewFlag("encoder", "e", "encode with base64, base64url, hex or raw", "hex")
fatal(err, "could not add encoder flag")
err = flags.NewFlag("output", "o", "output file to write the hashed password to", "stdout")
fatal(err, "could not add output flag")
err = flags.NewFlag("newline", "n", "add a newline to the end of the output", false)
fatal(err, "could not add show flag")
err = flags.NewFlag("show", "s", "show input when typing", false)
fatal(err, "could not add show flag")
err = flags.ParseArgs()
fatal(err, "could not parse flags")
}
func getEnc() func(src []byte) []byte {
encoder, err := flags.GetStringFlag("encoder")
fatal(err, "could not get encoder flag")
switch encoder {
case "base64":
return func(src []byte) []byte {
return []byte(base64.StdEncoding.EncodeToString(src))
}
case "base64url":
return func(src []byte) []byte {
return []byte(base64.URLEncoding.EncodeToString(src))
}
case "hex":
return func(src []byte) []byte {
return []byte(hex.EncodeToString(src))
}
case "raw":
return func(src []byte) []byte {
return src
}
default:
fatal(errors.New("unknown encoder: "+encoder), "invalid encoder")
}
return nil
}
func getHashFunc() hash.Hash {
algorithm, err := flags.GetStringFlag("algorithm")
fatal(err, "could not get algorithm flag")
switch algorithm {
case "sha1":
return sha1.New()
case "sha256":
return sha256.New()
case "sha512":
return sha512.New()
default:
fatal(errors.New("unknown algorithm: "+algorithm), "invalid algorithm")
}
return nil
}
func getOutFunc() func(w []byte) error {
output, err := flags.GetStringFlag("output")
fatal(err, "could not get output flag")
if output == "stdout" {
return func(w []byte) error {
_, e := os.Stdout.Write(w)
return e
}
}
if output != "" {
_, err = os.Stat(output)
if !errors.Is(err, os.ErrNotExist) {
fmt.Printf("output file already exists, do you want to overwrite it? (y/n) ")
line, _ := parsers.ReadLineString(os.Stdin)
if line == "y" {
fmt.Printf("\roverwriting: %s\n", output)
}
}
return func(w []byte) error {
f, e := os.Create(output)
if e != nil {
return e
}
_, e = f.Write(w)
f.Close()
return e
}
}
fatal(errors.New("invalid output: "+output), "invalid output")
return nil
}
func main() {
var err error
hashFunc := getHashFunc()
enc := getEnc()
out := getOutFunc()
var line bool
line, err = flags.GetBoolFlag("newline")
fatal(err, "could not get newline flag")
var show bool
show, err = flags.GetBoolFlag("show")
fatal(err, "could not get show flag")
var password []byte
fmt.Print("Enter password to hash: ")
if show {
password, err = parsers.ReadLine(os.Stdin)
fatal(err, "could not read password")
} else {
password, err = term.ReadPassword(0)
fmt.Println()
fatal(err, "could not read password")
fmt.Print("Enter password again: ")
var pas []byte
pas, err = term.ReadPassword(0)
fmt.Println()
fatal(err, "could not read password")
if string(pas) != string(password) {
fmt.Println("Passwords do not match, please try again.")
os.Exit(1)
}
}
if len(password) == 0 {
fatal(errors.New("password cannot be empty"), "invalid password")
}
var n int
n, err = hashFunc.Write(password)
fatal(err, "could not write password to hash function")
if n != len(password) {
fatal(errors.New("could not write all bytes to hash function"), "invalid password")
}
encHash := hashFunc.Sum(nil)
encHash = enc(encHash)
if line {
encHash = append(encHash, '\n')
}
err = out(encHash)
fatal(err, "could not write output")
}