Comment all new code
Some checks are pending
/ test (push) Waiting to run

This commit is contained in:
Melody Becker 2025-04-02 15:33:07 +02:00
parent b6f12b7acf
commit 8f8ad3035a
Signed by: mstar
SSH key fingerprint: SHA256:9VAo09aaVNTWKzPW7Hq2LW+ox9OdwmTSHRoD4mlz1yI
33 changed files with 166 additions and 111 deletions

View file

@ -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 (

View file

@ -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:

View file

@ -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,