This commit is contained in:
parent
b6f12b7acf
commit
8f8ad3035a
33 changed files with 166 additions and 111 deletions
|
@ -13,10 +13,13 @@ import (
|
|||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
)
|
||||
|
||||
// An Authenticator is used for authenticating user requests against the server
|
||||
type Authenticator struct {
|
||||
webauthn *webauthn.WebAuthn
|
||||
recentlyUsedTotpTokens map[string]time.Time
|
||||
}
|
||||
|
||||
// The next state of a login process
|
||||
type LoginNextState uint8
|
||||
|
||||
const (
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
// Len of salt for passwords in bytes
|
||||
const saltLen = 32
|
||||
|
||||
// Generate a random salt with the given nr of bytes
|
||||
func generateSalt(length int) ([]byte, error) {
|
||||
salt := make([]byte, length)
|
||||
if _, err := rand.Read(salt); err != nil {
|
||||
|
@ -26,18 +27,19 @@ func generateSalt(length int) ([]byte, error) {
|
|||
return salt, nil
|
||||
}
|
||||
|
||||
func hashPassword(password string) ([]byte, error) {
|
||||
// Hash a password with salt
|
||||
func hashPassword(password string) (hash []byte, err error) {
|
||||
salt, err := generateSalt(saltLen)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hash := argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
|
||||
hash = argon2.IDKey([]byte(password), salt, 1, 64*1024, 4, 32)
|
||||
hash = append(hash, salt...)
|
||||
// return bcrypt.GenerateFromPassword([]byte(password), 14)
|
||||
return hash, nil
|
||||
}
|
||||
|
||||
// Compare a raw password against a hash + salt
|
||||
// Check wether a password matches a hash
|
||||
func comparePassword(password string, hash []byte) bool {
|
||||
// Hash is actually hash(password)+salt
|
||||
salt := hash[len(hash)-saltLen:]
|
||||
|
@ -47,6 +49,7 @@ func comparePassword(password string, hash []byte) bool {
|
|||
|
||||
// Copied and adjusted from: https://bruinsslot.jp/post/golang-crypto/
|
||||
|
||||
// Encrypt some data using a key
|
||||
func Encrypt(key, data []byte) ([]byte, error) {
|
||||
key, salt, err := deriveKey(key, nil)
|
||||
if err != nil {
|
||||
|
@ -75,6 +78,7 @@ func Encrypt(key, data []byte) ([]byte, error) {
|
|||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// Decrypt some data using a key
|
||||
func Decrypt(key, data []byte) ([]byte, error) {
|
||||
salt, data := data[len(data)-32:], data[:len(data)-32]
|
||||
|
||||
|
@ -103,6 +107,8 @@ func Decrypt(key, data []byte) ([]byte, error) {
|
|||
return plaintext, nil
|
||||
}
|
||||
|
||||
// Derive a key from a password and optionally salt.
|
||||
// Returns the hash and salt used
|
||||
func deriveKey(password, salt []byte) ([]byte, []byte, error) {
|
||||
if salt == nil {
|
||||
salt = make([]byte, 32)
|
||||
|
@ -115,12 +121,16 @@ func deriveKey(password, salt []byte) ([]byte, []byte, error) {
|
|||
return key, salt, nil
|
||||
}
|
||||
|
||||
// Calculate the expiration timestamp from the call of the function
|
||||
func calcAccessExpirationTimestamp() time.Time {
|
||||
// For now, the default expiration is one month after creation
|
||||
// though "never" might also be a good option
|
||||
return time.Now().Add(time.Hour * 24 * 30)
|
||||
}
|
||||
|
||||
// Convert a list of authentication methods into a [LoginNextState] bitflag.
|
||||
// [isStart] determines whether to allow authentication methods that start
|
||||
// a login process or complete one
|
||||
func ConvertNewStorageAuthMethodsToLoginState(
|
||||
methods []models.AuthenticationMethodType,
|
||||
isStart bool,
|
||||
|
@ -141,6 +151,7 @@ func ConvertNewStorageAuthMethodsToLoginState(
|
|||
)
|
||||
}
|
||||
|
||||
// Translate one [models.AuthenticationMethodType] to one [LoginNextState]
|
||||
func oneStorageAuthToLoginState(in models.AuthenticationMethodType) LoginNextState {
|
||||
switch in {
|
||||
case models.AuthMethodGAuth:
|
||||
|
|
|
@ -20,6 +20,7 @@ import (
|
|||
const totpUnverifiedSuffix = "-NOT_VERIFIED"
|
||||
const totpTokenNoLongerRecentlyUsed = time.Second * 90
|
||||
|
||||
// Perform a 2nd factor totp based login
|
||||
func (a *Authenticator) PerformTotpLogin(
|
||||
username string,
|
||||
sessionId uint64,
|
||||
|
@ -119,6 +120,8 @@ func (a *Authenticator) PerformTotpLogin(
|
|||
return LoginNextSucess, token.Token, nil
|
||||
}
|
||||
|
||||
// Create a new totp key for a user.
|
||||
// The key is marked as not verified until it is sucessfully used once
|
||||
func (a *Authenticator) StartTotpRegistration(
|
||||
username string,
|
||||
tokenName string,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue