1
0
mirror of https://github.com/Luzifer/password.git synced 2024-09-19 18:32:57 +00:00

Implement JSON output with password hashes

Signed-off-by: Knut Ahlers <knut@ahlers.me>
This commit is contained in:
Knut Ahlers 2017-10-06 22:54:45 +02:00
parent 9998a6a138
commit 6a76611a12
Signed by: luzifer
GPG Key ID: DC2729FDD34BE99E
4 changed files with 199 additions and 1 deletions

View File

@ -1,9 +1,11 @@
package main package main
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"github.com/Luzifer/password/hasher"
"github.com/Luzifer/password/lib" "github.com/Luzifer/password/lib"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -15,6 +17,7 @@ func getCmdGet() *cobra.Command {
Run: actionCmdGet, Run: actionCmdGet,
} }
cmd.Flags().BoolVarP(&flags.CLI.JSON, "json", "j", false, "return output in JSON format")
cmd.Flags().IntVarP(&flags.CLI.Length, "length", "l", 20, "length of the generated password") cmd.Flags().IntVarP(&flags.CLI.Length, "length", "l", 20, "length of the generated password")
cmd.Flags().BoolVarP(&flags.CLI.SpecialCharacters, "special", "s", false, "use special characters in your password") cmd.Flags().BoolVarP(&flags.CLI.SpecialCharacters, "special", "s", false, "use special characters in your password")
@ -33,5 +36,16 @@ func actionCmdGet(cmd *cobra.Command, args []string) {
os.Exit(1) os.Exit(1)
} }
if !flags.CLI.JSON {
fmt.Println(password) fmt.Println(password)
os.Exit(0)
}
hashes, err := hasher.GetHashMap(password)
if err != nil {
fmt.Printf("Unable to generate hashes: %s", err)
os.Exit(1)
}
hashes["password"] = password
json.NewEncoder(os.Stdout).Encode(hashes)
} }

View File

@ -4,6 +4,7 @@ var flags = struct {
CLI struct { CLI struct {
Length int Length int
SpecialCharacters bool SpecialCharacters bool
JSON bool
} }
Server struct { Server struct {

94
hasher/hasher.go Normal file
View File

@ -0,0 +1,94 @@
package hasher
import (
"crypto"
"crypto/rand"
_ "crypto/sha1"
_ "crypto/sha256"
_ "crypto/sha512"
"fmt"
"math/big"
"golang.org/x/crypto/bcrypt"
"github.com/tredoe/osutil/user/crypt"
_ "github.com/tredoe/osutil/user/crypt/apr1_crypt"
_ "github.com/tredoe/osutil/user/crypt/sha256_crypt"
_ "github.com/tredoe/osutil/user/crypt/sha512_crypt"
)
type hasherFunc func(password string) (string, error)
var (
implementations = map[string]hasherFunc{
"htpasswd_apr1": implHTAPR1,
"htpasswd_bcrypt": implBcrypt,
"htpasswd_sha256": implHTSHA256,
"htpasswd_sha512": implHTSHA512,
"sha1": implSHA1,
"sha256": implSHA256,
"sha512": implSHA512,
}
saltSet = []byte(`abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789./`)
saltSize = 12
)
func GetHashMap(password string) (map[string]string, error) {
result := map[string]string{}
for name, hf := range implementations {
h, err := hf(password)
if err != nil {
return nil, err
}
result[name] = h
}
return result, nil
}
func getSalt() ([]byte, error) {
salt := make([]byte, saltSize)
saltSetLength := big.NewInt(int64(len(saltSet)))
for i := 0; i < saltSize; i++ {
pos, err := rand.Int(rand.Reader, saltSetLength)
if err != nil {
return nil, err
}
salt[i] = saltSet[pos.Int64()]
}
return salt, nil
}
func implBcrypt(password string) (string, error) {
bc, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return string(bc), err
}
func genericHT(password, prefix string) (string, error) {
salt, err := getSalt()
if err != nil {
return "", err
}
return crypt.NewFromHash(prefix+string(salt)).Generate([]byte(password), append([]byte(prefix), salt...))
}
func implHTAPR1(password string) (string, error) { return genericHT(password, "$apr1$") }
func implHTSHA256(password string) (string, error) { return genericHT(password, "$5$") }
func implHTSHA512(password string) (string, error) { return genericHT(password, "$6$") }
func generic(password string, h crypto.Hash) (string, error) {
w := h.New()
w.Write([]byte(password))
return fmt.Sprintf("%x", w.Sum(nil)), nil
}
func implSHA1(password string) (string, error) { return generic(password, crypto.SHA1) }
func implSHA256(password string) (string, error) { return generic(password, crypto.SHA256) }
func implSHA512(password string) (string, error) { return generic(password, crypto.SHA512) }

89
hasher/hasher_test.go Normal file
View File

@ -0,0 +1,89 @@
package hasher
import (
"strings"
"testing"
"github.com/Luzifer/go_helpers/str"
)
func TestAvailableHashs(t *testing.T) {
hashs, err := GetHashMap("testpass")
if err != nil {
t.Fatalf("Hash map generation failed: %s", err)
}
for _, impl := range []string{
"htpasswd_apr1",
"htpasswd_bcrypt",
"htpasswd_sha256",
"htpasswd_sha512",
"sha1",
"sha256",
"sha512",
} {
if _, ok := hashs[impl]; !ok {
t.Errorf("Hash implementation %q is missing", impl)
}
}
}
func TestSalt(t *testing.T) {
knownSalts := []string{}
for i := 0; i < 100; i++ {
salt, err := getSalt()
if err != nil {
t.Fatalf("Hash generation failed: %s", err)
}
if len(salt) != saltSize {
t.Errorf("Salt did not have desired size of %d: %d", saltSize, len(salt))
}
ssalt := string(salt)
if str.StringInSlice(ssalt, knownSalts) {
t.Fatalf("Received collision of hashes: %q", ssalt)
}
knownSalts = append(knownSalts, ssalt)
}
}
func TestHTPasswd(t *testing.T) {
hashs, err := GetHashMap("testpass")
if err != nil {
t.Fatalf("Hash map generation failed: %s", err)
}
if len(hashs["htpasswd_sha512"]) != 102 || !strings.HasPrefix(hashs["htpasswd_sha512"], "$6$") {
t.Errorf("Invalid htpasswd SHA512 hash: %q", hashs["htpasswd_sha512"])
}
if len(hashs["htpasswd_sha256"]) != 59 || !strings.HasPrefix(hashs["htpasswd_sha256"], "$5$") {
t.Errorf("Invalid htpasswd SHA256 hash: %q", hashs["htpasswd_sha256"])
}
if len(hashs["htpasswd_apr1"]) != 37 || !strings.HasPrefix(hashs["htpasswd_apr1"], "$apr1$") {
t.Errorf("Invalid htpasswd APR1 hash: %q", hashs["htpasswd_apr1"])
}
}
func TestStandardHashs(t *testing.T) {
hashs, err := GetHashMap("testpass")
if err != nil {
t.Fatalf("Hash map generation failed: %s", err)
}
if hashs["sha1"] != "206c80413b9a96c1312cc346b7d2517b84463edd" {
t.Errorf("Invalid SHA1 hash: %q", hashs["sha1"])
}
if hashs["sha256"] != "13d249f2cb4127b40cfa757866850278793f814ded3c587fe5889e889a7a9f6c" {
t.Errorf("Invalid SHA256 hash: %q", hashs["sha256"])
}
if hashs["sha512"] != "78ddc8555bb1677ff5af75ba5fc02cb30bb592b0610277ae15055e189b77fe3fda496e5027a3d99ec85d54941adee1cc174b50438fdc21d82d0a79f85b58cf44" {
t.Errorf("Invalid SHA512 hash: %q", hashs["sha512"])
}
}