Lightweight and easily self-hostable Git forge. git.poy.ooo
git-forge selfhosting homebrew
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118package lib
import ( "crypto/rand" "crypto/subtle" "encoding/base64" "errors" "fmt" "strings"
"golang.org/x/crypto/argon2")
var ( ErrInvalidHash = errors.New("Hash has an invalid format") ErrIncompatibleVersion = errors.New("Incompatible version of argon2"))
type Argon2idConfig struct { raw_hash []byte salt []byte
hash_len uint32 // bytes salt_len uint32 // bytes mem_cost uint32 // KiB iterations uint32 threads uint8}
func GenerateSalt(length int) ([]byte, error) { secret := make([]byte, length)
_, err := rand.Read(secret) if err != nil { return nil, err }
return secret, nil}
func GenerateHash(password string) (string, error) { var err error config := &Argon2idConfig{ iterations: 4, mem_cost: 64 * 1024, threads: 4, hash_len: 32, } config.salt, err = GenerateSalt(16) if err != nil { return "", err }
config.raw_hash = argon2.IDKey([]byte(password), config.salt, config.iterations, config.mem_cost, config.threads, config.hash_len) encoded_hash := fmt.Sprintf( "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", argon2.Version, config.mem_cost, config.iterations, config.threads, base64.RawStdEncoding.EncodeToString(config.salt), base64.RawStdEncoding.EncodeToString(config.raw_hash), )
return encoded_hash, nil}
func ValidateHash(password string, encoded_hash string) (match bool, err error) { p, salt, hash, err := DecodeHash(encoded_hash) if err != nil { return false, err }
otherHash := argon2.IDKey([]byte(password), salt, p.iterations, p.mem_cost, p.threads, p.hash_len)
// subtle.ConstantTimeCompare prevents timing attacks. if subtle.ConstantTimeCompare(hash, otherHash) == 1 { return true, nil } return false, nil}
func DecodeHash(encodedHash string) (p *Argon2idConfig, salt, hash []byte, err error) { vals := strings.Split(encodedHash, "$") if len(vals) != 6 { return nil, nil, nil, ErrInvalidHash }
var version int _, err = fmt.Sscanf(vals[2], "v=%d", &version) if err != nil { return nil, nil, nil, err } if version != argon2.Version { return nil, nil, nil, ErrIncompatibleVersion }
p = &Argon2idConfig{} _, err = fmt.Sscanf(vals[3], "m=%d,t=%d,p=%d", &p.mem_cost, &p.iterations, &p.threads) if err != nil { return nil, nil, nil, err }
salt, err = base64.RawStdEncoding.Strict().DecodeString(vals[4]) if err != nil { return nil, nil, nil, err }
hash, err = base64.RawStdEncoding.Strict().DecodeString(vals[5]) if err != nil { return nil, nil, nil, err } p.hash_len = uint32(len(hash)) p.salt_len = uint32(len(salt))
return p, salt, hash, nil}