I am writing a function to decode Argon2 hashed string to extract parameters. I need to handle many decode errors, and handle those errors lead to write many repeating if err == nil check code. Is there a better way to do this kind of check?
The encodedHash string follows the format, separated by $
"$argon2id$v=%d$m=%d,t=%d,p=%d$%s$%s"
so an example string looks like
$argon2id$v=19$m=65536,t=3,p=4$ruDgwQK24h0wGXI87+lVWAbHmgNidUNPVSTdSloOlfM$py20HR7L4K6LllGsZNDbkrbh89x2tIF8JCIG0DAaoi8
What the decode function do is convert the string into this struct
type Argon2Params struct {
Memory uint32
Time uint32
Threads uint8
SaltLength uint32
KeyLength uint32
}
Here is the decode function, I need to do a serious check during the decoding process. Every filed need to be checked to make sure no error rises.
func decodeHash(encodedHash string) (p *Params, salt, hash []byte, err error) {
fileds := strings.Split(encodedHash, "$")
if len(fileds) != 6 {
return nil, nil, nil, errors.New("The encoded hash is not in the correct format")
}
var version int
// error check
if _, err = fmt.Sscanf(fileds[2], "v=%d", &version); err != nil {
return
}
// error check
if version != argon2.Version {
return nil, nil, nil, errors.New("Argon2 version not match")
}
p = &Params{}
_, err = fmt.Sscanf(fileds[3], "m=%d,t=%d,p=%d", &p.memory, &p.time, &p.threads)
// error check
if err != nil {
return
}
salt, p.saltLength, err = decodeBase64WithLength(fileds[4])
// error check
if err != nil {
return
}
// error check
hash, p.keyLength, err = decodeBase64WithLength(fileds[5])
return
}
// a helper function
func decodeBase64WithLength(encodeString string) (str []byte, length uint32, err error) {
str, err = base64.RawStdEncoding.DecodeString(encodeString)
length = uint32(len(str))
return
}