mirror of
https://github.com/absmach/magistrala.git
synced 2026-06-23 04:10:28 +00:00
7f03134d8e
Property Based Tests / api-test (push) Has been cancelled
Continuous Delivery / lint-and-build (push) Has been cancelled
Deploy GitHub Pages / swagger-ui (push) Has been cancelled
CI Pipeline / Lint Proto (push) Has been cancelled
CI Pipeline / Detect Changes (push) Has been cancelled
Continuous Delivery / Build and Push Docker Images (push) Has been cancelled
CI Pipeline / lint-and-build (push) Has been cancelled
CI Pipeline / Test ${{ matrix.module }} (push) Has been cancelled
CI Pipeline / Upload Coverage (push) Has been cancelled
Signed-off-by: nyagamunene <stevenyaga2014@gmail.com> Signed-off-by: JeffMboya <jangina.mboya@gmail.com> Co-authored-by: JeffMboya <jangina.mboya@gmail.com>
95 lines
2.3 KiB
Go
95 lines
2.3 KiB
Go
// Copyright (c) Abstract Machines
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package hasher
|
|
|
|
import (
|
|
"crypto/subtle"
|
|
"encoding/base64"
|
|
"strings"
|
|
|
|
"github.com/absmach/magistrala/bootstrap"
|
|
"github.com/absmach/magistrala/pkg/errors"
|
|
"golang.org/x/crypto/bcrypt"
|
|
"golang.org/x/crypto/scrypt"
|
|
)
|
|
|
|
const (
|
|
cost = 10
|
|
legacyScryptPrefix = "scrypt$"
|
|
legacyScryptKeyN = 16384
|
|
legacyScryptKeyR = 8
|
|
legacyScryptKeyP = 1
|
|
legacyScryptKeySize = 32
|
|
)
|
|
|
|
var (
|
|
errHashExternalKey = errors.NewServiceError("generate hash from external key failed")
|
|
errCompareExternalKey = errors.NewServiceError("compare external key and hash failed")
|
|
errInvalidHashStore = errors.New("invalid stored external key hash format")
|
|
errDecode = errors.New("failed to decode external key hash")
|
|
)
|
|
|
|
var _ bootstrap.Hasher = (*bcryptHasher)(nil)
|
|
|
|
type bcryptHasher struct{}
|
|
|
|
// New instantiates a bcrypt-based hasher implementation.
|
|
func New() bootstrap.Hasher {
|
|
return &bcryptHasher{}
|
|
}
|
|
|
|
func (*bcryptHasher) Hash(key string) (string, error) {
|
|
hash, err := bcrypt.GenerateFromPassword([]byte(key), cost)
|
|
if err != nil {
|
|
return "", errors.Wrap(errHashExternalKey, err)
|
|
}
|
|
|
|
return string(hash), nil
|
|
}
|
|
|
|
func (*bcryptHasher) Compare(plain, hashed string) error {
|
|
if strings.HasPrefix(hashed, legacyScryptPrefix) {
|
|
return compareLegacyScryptHash(plain, hashed)
|
|
}
|
|
|
|
if err := bcrypt.CompareHashAndPassword([]byte(hashed), []byte(plain)); err == nil {
|
|
return nil
|
|
}
|
|
|
|
// Legacy rows may still contain plaintext external keys.
|
|
if subtle.ConstantTimeCompare([]byte(plain), []byte(hashed)) == 1 {
|
|
return nil
|
|
}
|
|
|
|
return bootstrap.ErrExternalKey
|
|
}
|
|
|
|
func compareLegacyScryptHash(plain, hashed string) error {
|
|
parts := strings.Split(strings.TrimPrefix(hashed, legacyScryptPrefix), ".")
|
|
if len(parts) != 2 {
|
|
return errInvalidHashStore
|
|
}
|
|
|
|
actualHash, err := base64.StdEncoding.DecodeString(parts[0])
|
|
if err != nil {
|
|
return errors.Wrap(errDecode, err)
|
|
}
|
|
|
|
salt, err := base64.StdEncoding.DecodeString(parts[1])
|
|
if err != nil {
|
|
return errors.Wrap(errDecode, err)
|
|
}
|
|
|
|
derivedHash, err := scrypt.Key([]byte(plain), salt, legacyScryptKeyN, legacyScryptKeyR, legacyScryptKeyP, legacyScryptKeySize)
|
|
if err != nil {
|
|
return errors.Wrap(errCompareExternalKey, err)
|
|
}
|
|
|
|
if subtle.ConstantTimeCompare(derivedHash, actualHash) == 1 {
|
|
return nil
|
|
}
|
|
|
|
return bootstrap.ErrExternalKey
|
|
}
|