github.com/goravel/framework@v1.13.9/hash/argon2id.go (about) 1 package hash 2 3 import ( 4 "crypto/rand" 5 "crypto/subtle" 6 "encoding/base64" 7 "fmt" 8 "strings" 9 10 "golang.org/x/crypto/argon2" 11 12 "github.com/goravel/framework/contracts/config" 13 ) 14 15 type Argon2id struct { 16 // The format of the hash. 17 format string 18 // The version of argon2id to use. 19 version int 20 // The time cost parameter. 21 time uint32 22 // The memory cost parameter. 23 memory uint32 24 // The threads cost parameter. 25 threads uint8 26 // The length of the key to generate. 27 keyLen uint32 28 // The length of the random salt to generate. 29 saltLen uint32 30 } 31 32 // NewArgon2id returns a new Argon2id hasher. 33 func NewArgon2id(config config.Config) *Argon2id { 34 return &Argon2id{ 35 format: "$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s", 36 version: argon2.Version, 37 time: uint32(config.GetInt("hashing.argon2id.time", 4)), 38 memory: uint32(config.GetInt("hashing.argon2id.memory", 65536)), 39 threads: uint8(config.GetInt("hashing.argon2id.threads", 1)), 40 keyLen: 32, 41 saltLen: 16, 42 } 43 } 44 45 func (a *Argon2id) Make(value string) (string, error) { 46 salt := make([]byte, a.saltLen) 47 if _, err := rand.Read(salt); err != nil { 48 return "", err 49 } 50 51 hash := argon2.IDKey([]byte(value), salt, a.time, a.memory, a.threads, a.keyLen) 52 53 return fmt.Sprintf(a.format, a.version, a.memory, a.time, a.threads, base64.RawStdEncoding.EncodeToString(salt), base64.RawStdEncoding.EncodeToString(hash)), nil 54 } 55 56 func (a *Argon2id) Check(value, hash string) bool { 57 hashParts := strings.Split(hash, "$") 58 if len(hashParts) != 6 { 59 return false 60 } 61 62 var version int 63 _, err := fmt.Sscanf(hashParts[2], "v=%d", &version) 64 if err != nil { 65 return false 66 } 67 if version != a.version { 68 return false 69 } 70 71 memory := a.memory 72 time := a.time 73 threads := a.threads 74 75 _, err = fmt.Sscanf(hashParts[3], "m=%d,t=%d,p=%d", &memory, &time, &threads) 76 if err != nil { 77 return false 78 } 79 80 salt, err := base64.RawStdEncoding.DecodeString(hashParts[4]) 81 if err != nil { 82 return false 83 } 84 85 decodedHash, err := base64.RawStdEncoding.DecodeString(hashParts[5]) 86 if err != nil { 87 return false 88 } 89 90 hashToCompare := argon2.IDKey([]byte(value), salt, time, memory, threads, uint32(len(decodedHash))) 91 92 return subtle.ConstantTimeCompare(decodedHash, hashToCompare) == 1 93 } 94 95 func (a *Argon2id) NeedsRehash(hash string) bool { 96 hashParts := strings.Split(hash, "$") 97 if len(hashParts) != 6 { 98 return true 99 } 100 101 var version int 102 _, err := fmt.Sscanf(hashParts[2], "v=%d", &version) 103 if err != nil { 104 return true 105 } 106 if version != a.version { 107 return true 108 } 109 110 var memory, time uint32 111 var threads uint8 112 _, err = fmt.Sscanf(hashParts[3], "m=%d,t=%d,p=%d", &memory, &time, &threads) 113 if err != nil { 114 return true 115 } 116 117 return memory != a.memory || time != a.time || threads != a.threads 118 }