WIP: Add users system, required for uploading new pastes #77
							
								
								
									
										72
									
								
								internal/db/password.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										72
									
								
								internal/db/password.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,72 @@
 | 
				
			|||||||
 | 
					package db
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"crypto/rand"
 | 
				
			||||||
 | 
						"crypto/subtle"
 | 
				
			||||||
 | 
						"encoding/base64"
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"golang.org/x/crypto/argon2"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const (
 | 
				
			||||||
 | 
						pwdSaltSize = 16
 | 
				
			||||||
 | 
						pwdHashSize = 32
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// chosen from https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
 | 
				
			||||||
 | 
						pwdMemory   = 64 * 1024
 | 
				
			||||||
 | 
						pwdThreads  = 1
 | 
				
			||||||
 | 
						pwdIterations = 2
 | 
				
			||||||
 | 
						pwdParams   = "m=65536,t=2,p=1,v=19"
 | 
				
			||||||
 | 
						pwdVersion  = "v=19"
 | 
				
			||||||
 | 
						pwdAlgo     = "argon2id"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func HashPassword(password string) (string, error) {
 | 
				
			||||||
 | 
						if argon2.Version != 19 {
 | 
				
			||||||
 | 
							// go has no static asserts
 | 
				
			||||||
 | 
							panic("Unexpected argon2 version")
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						// Generate a salt for the password hash
 | 
				
			||||||
 | 
						salt := make([]byte, pwdSaltSize)
 | 
				
			||||||
 | 
						if _, err := rand.Read(salt); err != nil {
 | 
				
			||||||
 | 
							return "", err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Hash the password using argon2id
 | 
				
			||||||
 | 
						hash := argon2.IDKey([]byte(password), salt, pwdIterations, pwdMemory, pwdThreads, pwdHashSize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Encode the salt and hash as a string in PHC format
 | 
				
			||||||
 | 
						encodedSalt := base64.RawStdEncoding.EncodeToString(salt)
 | 
				
			||||||
 | 
						encodedHash := base64.RawStdEncoding.EncodeToString(hash)
 | 
				
			||||||
 | 
						return fmt.Sprintf("$%s$%s$%s$%s$%s", pwdAlgo, pwdVersion, pwdParams, encodedSalt, encodedHash), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var errInvalidDBPasswordFormat = errors.New("invalid password format in db")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func comparePassword(hashedPassword string, password string) (bool, error) {
 | 
				
			||||||
 | 
						// Extract the salt and hash from the hashed password string
 | 
				
			||||||
 | 
						fields := strings.Split(hashedPassword, "$")
 | 
				
			||||||
 | 
						if len(fields) != 6 || fields[1] != pwdAlgo || fields[2] != pwdVersion || fields[3] != pwdParams {
 | 
				
			||||||
 | 
							return false, errInvalidDBPasswordFormat
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						encodedSalt, encodedHash := fields[4], fields[5]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Decode the salt and hash from base64
 | 
				
			||||||
 | 
						salt, err := base64.RawStdEncoding.DecodeString(encodedSalt)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						hash, err := base64.RawStdEncoding.DecodeString(encodedHash)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Hash the password using the extracted salt and parameters
 | 
				
			||||||
 | 
						computedHash := argon2.IDKey([]byte(password), salt, pwdIterations, pwdMemory, pwdThreads, pwdHashSize)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Compare the computed hash with the stored hash
 | 
				
			||||||
 | 
						return subtle.ConstantTimeCompare(hash, computedHash) == 1, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -2,15 +2,12 @@ package db
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"crypto/rand"
 | 
						"crypto/rand"
 | 
				
			||||||
	"crypto/subtle"
 | 
					 | 
				
			||||||
	"encoding/base64"
 | 
						"encoding/base64"
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"strings"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/google/uuid"
 | 
						"github.com/google/uuid"
 | 
				
			||||||
	"golang.org/x/crypto/argon2"
 | 
					 | 
				
			||||||
	"gorm.io/gorm"
 | 
						"gorm.io/gorm"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -66,65 +63,6 @@ func Authenticate(db *gorm.DB, username string, password string) (*User, error)
 | 
				
			|||||||
	return &user, nil
 | 
						return &user, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					 | 
				
			||||||
	pwdSaltSize = 16
 | 
					 | 
				
			||||||
	pwdHashSize = 32
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// chosen from https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html
 | 
					 | 
				
			||||||
	pwdMemory   = 64 * 1024
 | 
					 | 
				
			||||||
	pwdThreads  = 1
 | 
					 | 
				
			||||||
	pwdIterations = 2
 | 
					 | 
				
			||||||
	pwdParams   = "m=65536,t=2,p=1,v=19"
 | 
					 | 
				
			||||||
	pwdVersion  = "v=19"
 | 
					 | 
				
			||||||
	pwdAlgo     = "argon2id"
 | 
					 | 
				
			||||||
)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func HashPassword(password string) (string, error) {
 | 
					 | 
				
			||||||
	if argon2.Version != 19 {
 | 
					 | 
				
			||||||
		// go has no static asserts
 | 
					 | 
				
			||||||
		panic("Unexpected argon2 version")
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	// Generate a salt for the password hash
 | 
					 | 
				
			||||||
	salt := make([]byte, pwdSaltSize)
 | 
					 | 
				
			||||||
	if _, err := rand.Read(salt); err != nil {
 | 
					 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Hash the password using argon2id
 | 
					 | 
				
			||||||
	hash := argon2.IDKey([]byte(password), salt, pwdIterations, pwdMemory, pwdThreads, pwdHashSize)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Encode the salt and hash as a string in PHC format
 | 
					 | 
				
			||||||
	encodedSalt := base64.RawStdEncoding.EncodeToString(salt)
 | 
					 | 
				
			||||||
	encodedHash := base64.RawStdEncoding.EncodeToString(hash)
 | 
					 | 
				
			||||||
	return fmt.Sprintf("$%s$%s$%s$%s$%s", pwdAlgo, pwdVersion, pwdParams, encodedSalt, encodedHash), nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var errInvalidDBPasswordFormat = errors.New("invalid password format in db")
 | 
					 | 
				
			||||||
func comparePassword(hashedPassword string, password string) (bool, error) {
 | 
					 | 
				
			||||||
	// Extract the salt and hash from the hashed password string
 | 
					 | 
				
			||||||
	fields := strings.Split(hashedPassword, "$")
 | 
					 | 
				
			||||||
	if len(fields) != 6 || fields[1] != pwdAlgo || fields[2] != pwdVersion || fields[3] != pwdParams {
 | 
					 | 
				
			||||||
		return false, errInvalidDBPasswordFormat
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	encodedSalt, encodedHash := fields[4], fields[5]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Decode the salt and hash from base64
 | 
					 | 
				
			||||||
	salt, err := base64.RawStdEncoding.DecodeString(encodedSalt)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return false, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	hash, err := base64.RawStdEncoding.DecodeString(encodedHash)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
					 | 
				
			||||||
		return false, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Hash the password using the extracted salt and parameters
 | 
					 | 
				
			||||||
	computedHash := argon2.IDKey([]byte(password), salt, pwdIterations, pwdMemory, pwdThreads, pwdHashSize)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// Compare the computed hash with the stored hash
 | 
					 | 
				
			||||||
	return subtle.ConstantTimeCompare(hash, computedHash) == 1, nil
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// DeleteUser deletes a user with the specified username from the database.
 | 
					// DeleteUser deletes a user with the specified username from the database.
 | 
				
			||||||
func DeleteUser(db *gorm.DB, username string) error {
 | 
					func DeleteUser(db *gorm.DB, username string) error {
 | 
				
			||||||
	// Find the user by username
 | 
						// Find the user by username
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user