103 lines
2.5 KiB
Go
103 lines
2.5 KiB
Go
package users
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/rand"
|
|
"encoding/base64"
|
|
"io"
|
|
"log"
|
|
|
|
fbErrors "github.com/filebrowser/filebrowser/v2/errors"
|
|
"github.com/pquerna/otp/totp"
|
|
"golang.org/x/crypto/bcrypt"
|
|
)
|
|
|
|
// HashPwd hashes a password.
|
|
func HashPwd(password string) (string, error) {
|
|
bytes, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
|
return string(bytes), err
|
|
}
|
|
|
|
// CheckPwd checks if a password is correct.
|
|
func CheckPwd(password, hash string) bool {
|
|
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
|
return err == nil
|
|
}
|
|
|
|
// returns cipher text and nonce in base64
|
|
func EncryptSymmetric(encryptionKey, secret []byte) (string, string, error) {
|
|
if len(encryptionKey) != 32 {
|
|
log.Printf("%s (key=\"%s\")", fbErrors.ErrInvalidEncryptionKey.Error(), string(encryptionKey))
|
|
return "", "", fbErrors.ErrInvalidEncryptionKey
|
|
}
|
|
|
|
block, err := aes.NewCipher(encryptionKey)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
nonce := make([]byte, gcm.NonceSize())
|
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
|
return "", "", err
|
|
}
|
|
|
|
cipherText := gcm.Seal(nil, nonce, secret, nil)
|
|
|
|
return base64.StdEncoding.EncodeToString(cipherText), base64.StdEncoding.EncodeToString(nonce), nil
|
|
}
|
|
|
|
func DecryptSymmetric(encryptionKey []byte, cipherTextB64, nonceB64 string) (string, error) {
|
|
if len(encryptionKey) != 32 {
|
|
log.Printf("%s (key=\"%s\")", fbErrors.ErrInvalidEncryptionKey.Error(), string(encryptionKey))
|
|
return "", fbErrors.ErrInvalidEncryptionKey
|
|
}
|
|
|
|
cipherText, err := base64.StdEncoding.DecodeString(cipherTextB64)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
nonce, err := base64.StdEncoding.DecodeString(nonceB64)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
block, err := aes.NewCipher(encryptionKey)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
gcm, err := cipher.NewGCM(block)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
secret, err := gcm.Open(nil, nonce, cipherText, nil)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return string(secret), nil
|
|
}
|
|
|
|
// Decrypt the secret and validate the code
|
|
func CheckTOTP(totpEncryptionKey []byte, encryptedSecretB64, nonceB64, code string) (bool, error) {
|
|
if len(totpEncryptionKey) != 32 {
|
|
log.Printf("%s (key=\"%s\")", fbErrors.ErrInvalidEncryptionKey.Error(), string(totpEncryptionKey))
|
|
return false, fbErrors.ErrInvalidEncryptionKey
|
|
}
|
|
|
|
secret, err := DecryptSymmetric(totpEncryptionKey, encryptedSecretB64, nonceB64)
|
|
if err != nil {
|
|
return false, err
|
|
}
|
|
|
|
return totp.Validate(code, secret), nil
|
|
}
|