69 lines
1.7 KiB
Go
69 lines
1.7 KiB
Go
package ssh
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"fmt"
|
|
"math/big"
|
|
)
|
|
|
|
const (
|
|
lowercaseChars = "abcdefghijklmnopqrstuvwxyz"
|
|
uppercaseChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
digitChars = "0123456789"
|
|
// Exclude shell-problematic characters: $ ` \ " '
|
|
// $ causes variable expansion, ` is command substitution, \ is escape, " and ' are quotes
|
|
// These can cause issues when used in shell commands or when copying from terminal
|
|
specialChars = "!@#%^&*()_+-=[]{}|;:,.<>?"
|
|
allChars = lowercaseChars + uppercaseChars + digitChars + specialChars
|
|
)
|
|
|
|
func GenerateSecurePassphrase(length int) (string, error) {
|
|
if length < 32 {
|
|
length = 32
|
|
}
|
|
|
|
passphrase := make([]byte, length)
|
|
|
|
for i := 0; i < length; i++ {
|
|
idx, err := rand.Int(rand.Reader, big.NewInt(int64(len(allChars))))
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to generate random character: %w", err)
|
|
}
|
|
passphrase[i] = allChars[idx.Int64()]
|
|
}
|
|
|
|
passphraseStr := string(passphrase)
|
|
|
|
if err := validatePassphrase(passphraseStr); err != nil {
|
|
return "", fmt.Errorf("generated passphrase failed validation: %w", err)
|
|
}
|
|
|
|
return passphraseStr, nil
|
|
}
|
|
|
|
func validatePassphrase(passphrase string) error {
|
|
hasLower := false
|
|
hasUpper := false
|
|
hasDigit := false
|
|
hasSpecial := false
|
|
|
|
for _, char := range passphrase {
|
|
switch {
|
|
case 'a' <= char && char <= 'z':
|
|
hasLower = true
|
|
case 'A' <= char && char <= 'Z':
|
|
hasUpper = true
|
|
case '0' <= char && char <= '9':
|
|
hasDigit = true
|
|
default:
|
|
hasSpecial = true
|
|
}
|
|
}
|
|
|
|
if !hasLower || !hasUpper || !hasDigit || !hasSpecial {
|
|
return fmt.Errorf("passphrase must contain at least one lowercase, uppercase, digit, and special character")
|
|
}
|
|
|
|
return nil
|
|
}
|