Personal Git forge.

Code git clone ssh://git.poy.ooo:6969/~poyo/farga ZIP
lib/crypto.go / 3KiB #5f46cc8d
Raw
package libimport (	"crypto/rand"	"crypto/subtle"	"encoding/base32"	"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}// generates an argon2id hashfunc 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}// generates an alphanumeric token with a given length.func AlphanumericToken(length uint) string {	randomBytes := make([]byte, length)	_, err := rand.Read(randomBytes)	if err != nil {		panic(err)	}	// this contraption makes sure to not trim unicode chars, for instance.	return string([]rune(base32.StdEncoding.EncodeToString(randomBytes))[:length])}// generates a base64-encoded token with a given length.func Base64Token(length uint) string {	randomBytes := make([]byte, length)	_, err := rand.Read(randomBytes)	if err != nil {		panic(err)	}	return base64.StdEncoding.EncodeToString(randomBytes)[:length]}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}