Skip to content

Commit

Permalink
优化 argon2
Browse files Browse the repository at this point in the history
  • Loading branch information
deatil committed Dec 27, 2024
1 parent 7582611 commit 5e8fd3a
Show file tree
Hide file tree
Showing 4 changed files with 185 additions and 93 deletions.
74 changes: 56 additions & 18 deletions passhash/argon2/argon2.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,36 @@ import (
"crypto/subtle"
"encoding/base64"

"golang.org/x/crypto/argon2"
"github.com/deatil/go-cryptobin/kdf/argon2"
)

// 配置
type Opt struct {
// Argon2 Type enum
type Argon2Type uint

func (typ Argon2Type) String() string {
switch typ {
case Argon2d:
return "argon2d"
case Argon2i:
return "argon2i"
case Argon2id:
return "argon2id"
case Argon2:
return "argon2"
default:
return "unknown multiple value " + strconv.Itoa(int(typ))
}
}

const (
Argon2d Argon2Type = iota
Argon2i
Argon2id
Argon2
)

// Argon2 options
type Opts struct {
SaltLen int
Time uint32
Memory uint32
Expand All @@ -23,11 +48,11 @@ type Opt struct {
}

var (
// 默认类型
defaultType = "argon2id"
// default Type
defaultType = Argon2id

// 默认配置
defaultOpt = Opt{
// default Options
defaultOpts = Opts{
SaltLen: 32,
Time: 1,
Memory: 64 * 1024,
Expand All @@ -36,18 +61,18 @@ var (
}
)

// 生成密钥
// Generate Salted Hash
func GenerateSaltedHash(random io.Reader, password string) (string, error) {
return GenerateSaltedHashWithTypeAndOpt(random, password, defaultType, defaultOpt)
return GenerateSaltedHashWithTypeAndOpts(random, password, defaultType, defaultOpts)
}

// 生成密钥带类型
func GenerateSaltedHashWithType(random io.Reader, password string, typ string) (string, error) {
return GenerateSaltedHashWithTypeAndOpt(random, password, typ, defaultOpt)
// Generate Salted Hash with type
func GenerateSaltedHashWithType(random io.Reader, password string, typ Argon2Type) (string, error) {
return GenerateSaltedHashWithTypeAndOpts(random, password, typ, defaultOpts)
}

// 生成密钥带类型和设置
func GenerateSaltedHashWithTypeAndOpt(random io.Reader, password string, typ string, opt Opt) (string, error) {
// Generate Salted Hash with type and opts
func GenerateSaltedHashWithTypeAndOpts(random io.Reader, password string, typ Argon2Type, opt Opts) (string, error) {
if len(password) == 0 {
return "", errors.New("go-cryptobin/argon2: Password length cannot be 0")
}
Expand All @@ -62,18 +87,24 @@ func GenerateSaltedHashWithTypeAndOpt(random io.Reader, password string, typ str

var unencodedPassword []byte
switch typ {
case "argon2id":
case Argon2id:
unencodedPassword = argon2.IDKey(
[]byte(password), []byte(salt),
argon2Time, argon2Memory,
argon2Threads, argon2KeyLen,
)
case "argon2i", "argon2":
case Argon2i, Argon2:
unencodedPassword = argon2.Key(
[]byte(password), []byte(salt),
argon2Time, argon2Memory,
argon2Threads, argon2KeyLen,
)
case Argon2d:
unencodedPassword = argon2.DKey(
[]byte(password), []byte(salt),
argon2Time, argon2Memory,
argon2Threads, argon2KeyLen,
)
default:
return "", errors.New("go-cryptobin/argon2: Invalid Hash Type")
}
Expand All @@ -82,7 +113,7 @@ func GenerateSaltedHashWithTypeAndOpt(random io.Reader, password string, typ str

hash := fmt.Sprintf(
"%s$%d$%d$%d$%d$%s$%s",
typ, argon2Time,
typ.String(), argon2Time,
argon2Memory, argon2Threads,
argon2KeyLen, salt,
encodedPassword,
Expand All @@ -91,7 +122,7 @@ func GenerateSaltedHashWithTypeAndOpt(random io.Reader, password string, typ str
return hash, nil
}

// 验证密钥
// Compare Hash With Password
func CompareHashWithPassword(hash, password string) (bool, error) {
if len(hash) == 0 || len(password) == 0 {
return false, errors.New("go-cryptobin/argon2: Arguments cannot be zero length")
Expand Down Expand Up @@ -124,6 +155,12 @@ func CompareHashWithPassword(hash, password string) (bool, error) {
uint32(time), uint32(memory),
uint8(threads), uint32(keyLen),
)
case "argon2d":
calculatedKey = argon2.DKey(
[]byte(password), salt,
uint32(time), uint32(memory),
uint8(threads), uint32(keyLen),
)
default:
return false, errors.New("go-cryptobin/argon2: Invalid Password Hash")
}
Expand All @@ -135,6 +172,7 @@ func CompareHashWithPassword(hash, password string) (bool, error) {
return true, nil
}

// generate salt with length
func generateSalt(random io.Reader, length int) (string, error) {
unencodedSalt := make([]byte, length)

Expand Down
20 changes: 13 additions & 7 deletions passhash/argon2/argon2_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ func Test_GenerateSaltedHash(t *testing.T) {
t.Errorf("GenerateSaltedHash() = %v, want %v", err, tt.wantErr)
return
}

if len(hashSegments) != tt.hashSegments {
t.Errorf("GenerateSaltedHash() had %d segments. Want %d", len(hashSegments), tt.hashSegments)
}
Expand All @@ -52,6 +53,7 @@ func Test_CompareHashWithPassword(t *testing.T) {
{"Should Work 2", `argon2$4$32768$4$32$/WN2BY5NDzVlHYgw3pqahA==$oLGdDy23gAgbQXmphVVPG0Uax+XbfeUfH/TCpQbEHfc=`, `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, true, false},
{"Should Not Work 4", `argon2$4$32768$4$32$/WN2BY5NDzVlHYgw3pqahA==$XLGdDy23gAgbQXmphVVPG0Uax+XbfeUfH/TCpQbEHfc=`, `Y&XEA)_m7q@jb@J"<sXrS]HH"zU`, false, true},
{"Should Not Work 5", `argon2$32768$4$32$/WN2BY5NDzVlHYgw3pqahA==$XLGdDy23gAgbQXmphVVPG0Uax+XbfeUfH/TCpQbEHfc=`, `Y&XEA)_m7q@jb@J"<sXrS]HH"zU`, false, true},
{"Should Work 3", `argon2d$1$65536$4$32$9oPeGkhyrfkDBbrGO8Kp4QEurO7dXuJz7V02/4xzzUY=$dlWao4wXOuKsBESejJsfcCwqA+g7/jc5tKK+z7yxdBE=`, `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, true, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand All @@ -72,18 +74,22 @@ func Test_CompareHashWithPassword(t *testing.T) {
func Test_GenerateSaltedHashWithType(t *testing.T) {
tests := []struct {
name string
typ string
typ Argon2Type
password string
hashSegments int
hashLength int
wantErr bool
}{
{"Should Work", "argon2id", "Password1", 7, 111, false},
{"Should Not Work", "argon2id", "", 1, 0, true},
{"Should Work 2", "argon2id", "gS</5Tu>3@(<FCtY", 7, 111, false},
{"Should Work 3", "argon2id", `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, 7, 111, false},
{"Should Work 31", "argon2i", `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, 7, 110, false},
{"Should Not Work 2", "argon2i", "", 1, 0, true},
{"Should Work", Argon2id, "Password1", 7, 111, false},
{"Should Not Work", Argon2id, "", 1, 0, true},
{"Should Work 2", Argon2id, "gS</5Tu>3@(<FCtY", 7, 111, false},
{"Should Work 3", Argon2id, `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, 7, 111, false},
{"Should Work 31", Argon2i, `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, 7, 110, false},
{"Should Not Work 2", Argon2i, "", 1, 0, true},
{"Should Work 5", Argon2d, `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, 7, 110, false},
{"Should Not Work 3", Argon2d, "", 1, 0, true},
{"Should Work 6", Argon2, `Y&jEA)_m7q@jb@J"<sXrS]HH"zU`, 7, 109, false},
{"Should Not Work 6", Argon2, "", 1, 0, true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
Expand Down
57 changes: 40 additions & 17 deletions passhash/argon2fmt/argon2fmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,30 @@ import (
"github.com/deatil/go-cryptobin/kdf/argon2"
)

// 配置
type Opt struct {
// Argon2 Type enum
type Argon2Type uint

func (typ Argon2Type) String() string {
switch typ {
case Argon2d:
return "argon2d"
case Argon2i:
return "argon2i"
case Argon2id:
return "argon2id"
default:
return "unknown multiple value " + strconv.Itoa(int(typ))
}
}

const (
Argon2d Argon2Type = iota
Argon2i
Argon2id
)

// Argon2 options
type Opts struct {
SaltLen int
Time uint32
Memory uint32
Expand All @@ -23,11 +45,11 @@ type Opt struct {
}

var (
// 默认类型
defaultType = "argon2id"
// default Type
defaultType = Argon2id

// 默认配置
defaultOpt = Opt{
// default Options
defaultOpts = Opts{
SaltLen: 32,
Time: 1,
Memory: 64 * 1024,
Expand All @@ -36,18 +58,18 @@ var (
}
)

// 生成密钥
// Generate Salted Hash
func GenerateSaltedHash(random io.Reader, password string) (string, error) {
return GenerateSaltedHashWithTypeAndOpt(random, password, defaultType, defaultOpt)
return GenerateSaltedHashWithTypeAndOpts(random, password, defaultType, defaultOpts)
}

// 生成密钥带类型
func GenerateSaltedHashWithType(random io.Reader, password string, typ string) (string, error) {
return GenerateSaltedHashWithTypeAndOpt(random, password, typ, defaultOpt)
// Generate Salted Hash with type
func GenerateSaltedHashWithType(random io.Reader, password string, typ Argon2Type) (string, error) {
return GenerateSaltedHashWithTypeAndOpts(random, password, typ, defaultOpts)
}

// 生成密钥带类型和设置
func GenerateSaltedHashWithTypeAndOpt(random io.Reader, password string, typ string, opt Opt) (string, error) {
// Generate Salted Hash with type and opts
func GenerateSaltedHashWithTypeAndOpts(random io.Reader, password string, typ Argon2Type, opt Opts) (string, error) {
if len(password) == 0 {
return "", errors.New("go-cryptobin/argon2fmt: Password length cannot be 0")
}
Expand All @@ -65,19 +87,19 @@ func GenerateSaltedHashWithTypeAndOpt(random io.Reader, password string, typ str

var unencodedPassword []byte
switch typ {
case "argon2id":
case Argon2id:
unencodedPassword = argon2.IDKey(
[]byte(password), salt,
argon2Time, argon2Memory,
argon2Threads, argon2KeyLen,
)
case "argon2i":
case Argon2i:
unencodedPassword = argon2.Key(
[]byte(password), salt,
argon2Time, argon2Memory,
argon2Threads, argon2KeyLen,
)
case "argon2d":
case Argon2d:
unencodedPassword = argon2.DKey(
[]byte(password), salt,
argon2Time, argon2Memory,
Expand All @@ -100,7 +122,7 @@ func GenerateSaltedHashWithTypeAndOpt(random io.Reader, password string, typ str
return hash, nil
}

// 验证密钥
// Compare Hash With Password
func CompareHashWithPassword(hash, password string) (bool, error) {
if len(hash) == 0 || len(password) == 0 {
return false, errors.New("go-cryptobin/argon2fmt: Arguments cannot be zero length")
Expand Down Expand Up @@ -179,6 +201,7 @@ func CompareHashWithPassword(hash, password string) (bool, error) {
return true, nil
}

// generate salt with length
func generateSalt(random io.Reader, length int) ([]byte, error) {
salt := make([]byte, length)
_, err := io.ReadFull(random, salt)
Expand Down
Loading

0 comments on commit 5e8fd3a

Please sign in to comment.