filebrowser/users/password.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
}