forked from toolshed/abra
		
	
		
			
				
	
	
		
			109 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			109 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package passgen
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"math"
 | 
						|
	"strings"
 | 
						|
)
 | 
						|
 | 
						|
// GeneratePasswords generates random passwords based on the configuration provided by the user.
 | 
						|
func GeneratePasswords(
 | 
						|
	count uint, // Number of passwords to generate.
 | 
						|
	length uint, // Length of each generated password.
 | 
						|
	alphabet string, // Alphabet to pull password characters from.
 | 
						|
) (
 | 
						|
	passwords []string, // Generated passwords.
 | 
						|
	err error, // Possible error encountered during password generation.
 | 
						|
) {
 | 
						|
	// Validate the supplied count parameter.
 | 
						|
	if count < PasswordCountMin || count > PasswordCountMax {
 | 
						|
		return nil, fmt.Errorf("count must be at least %d and at most %d", PasswordCountMin, PasswordCountMax)
 | 
						|
	}
 | 
						|
 | 
						|
	// Validate the supplied length parameter.
 | 
						|
	if length < PasswordLengthMin || length > PasswordLengthMax {
 | 
						|
		return nil, fmt.Errorf("length must be at least %d and at most %d", PasswordLengthMin, PasswordLengthMax)
 | 
						|
	}
 | 
						|
 | 
						|
	// Deduplicate the provided alphabet.
 | 
						|
	chars := map[rune]struct{}{}
 | 
						|
	for _, char := range alphabet {
 | 
						|
		chars[char] = struct{}{}
 | 
						|
	}
 | 
						|
	var charSet []rune
 | 
						|
	for char := range chars {
 | 
						|
		charSet = append(charSet, char)
 | 
						|
	}
 | 
						|
 | 
						|
	// Validate the provided alphabet.
 | 
						|
	if len(charSet) < AlphabetLengthMin {
 | 
						|
		return nil, fmt.Errorf("alphabet must contain at least %d unique characters", AlphabetLengthMin)
 | 
						|
	}
 | 
						|
 | 
						|
	// Determine how many bytes are needed to represent a password of the specified length in the
 | 
						|
	// provided alphabet.
 | 
						|
	bitsPerChar := uint(math.Ceil(math.Log2(float64(len(charSet)))))
 | 
						|
	bitsPerPassword := bitsPerChar * length
 | 
						|
	bytesPerPassword := bitsPerPassword / 8
 | 
						|
	if bitsPerPassword%8 > 0 {
 | 
						|
		bytesPerPassword++
 | 
						|
	}
 | 
						|
 | 
						|
	var (
 | 
						|
		i              uint            // Password counter.
 | 
						|
		b              strings.Builder // String builder for efficiently constructing passwords.
 | 
						|
		passwordBuffer []byte          // Byte buffer for random data used as password source.
 | 
						|
	)
 | 
						|
 | 
						|
	for i = 0; i < count; i++ {
 | 
						|
		// Read enough random data to sufficiently produce a password.
 | 
						|
		passwordBuffer = make([]byte, bytesPerPassword)
 | 
						|
		_, err = io.ReadFull(randSource, passwordBuffer)
 | 
						|
		if err != nil {
 | 
						|
			return nil, err
 | 
						|
		}
 | 
						|
 | 
						|
		var (
 | 
						|
			j          uint // Password character counter.
 | 
						|
			charIdx    uint // Character index within the provided alphabet.
 | 
						|
			bitIdx     uint // Source buffer bit counter.
 | 
						|
			byteIdx    uint // Source buffer byte counter.
 | 
						|
			charBitIdx uint // Password character bit counter.
 | 
						|
		)
 | 
						|
 | 
						|
		for j = 0; j < length; j++ {
 | 
						|
			for charBitIdx = 0; charBitIdx < bitsPerChar; charBitIdx++ {
 | 
						|
				// Left shift the character index to read the next bit.
 | 
						|
				charIdx <<= 1
 | 
						|
 | 
						|
				// Set the bit from the password source in the character index.
 | 
						|
				charIdx |= uint((passwordBuffer[byteIdx] & (0x80 >> (bitIdx % 8))) >> (7 - (bitIdx % 8)))
 | 
						|
 | 
						|
				// Increment the bit counter and, if necessary, the byte counter.
 | 
						|
				bitIdx++
 | 
						|
				if bitIdx%8 == 0 {
 | 
						|
					byteIdx++
 | 
						|
				}
 | 
						|
			}
 | 
						|
 | 
						|
			// Ensure the character index is within the bounds of the alphabet.
 | 
						|
			charIdx = charIdx % uint(len(charSet))
 | 
						|
 | 
						|
			// Retrieve the character from the alphabet and write it to the password.
 | 
						|
			b.WriteRune(charSet[charIdx])
 | 
						|
 | 
						|
			// Reset character index value.
 | 
						|
			charIdx = 0
 | 
						|
		}
 | 
						|
 | 
						|
		// Append the password to the return list.
 | 
						|
		passwords = append(passwords, b.String())
 | 
						|
 | 
						|
		// Reset the string builder for the next password.
 | 
						|
		b.Reset()
 | 
						|
	}
 | 
						|
 | 
						|
	return
 | 
						|
}
 |