refactor: remove math/big, fix RNG bias, optimize builds, and bump Go version

This commit is contained in:
Max
2026-01-04 22:10:28 -06:00
parent fe11983dab
commit c979561fa5
29 changed files with 57 additions and 27 deletions
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -1
View File
@@ -1,3 +1,3 @@
module GoPass module GoPass
go 1.24.1 go 1.25.5
+42 -12
View File
@@ -5,7 +5,6 @@ package main
import ( import (
"crypto/rand" "crypto/rand"
"fmt" "fmt"
"math/big"
"os" "os"
"runtime" "runtime"
"strconv" "strconv"
@@ -15,7 +14,7 @@ import (
var allowedCharacters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`~!@#$%^&*()_+[]\\{}|;':,./<>?") var allowedCharacters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890`~!@#$%^&*()_+[]\\{}|;':,./<>?")
const ( const (
Version = "1.3.4" Version = "1.3.5"
symbols = "`~!@#$%^&*()_+[]\\{}|;':,./<>?" symbols = "`~!@#$%^&*()_+[]\\{}|;':,./<>?"
chunkSize = 16 // The size of each chunk of the password to be generated by the worker goroutines chunkSize = 16 // The size of each chunk of the password to be generated by the worker goroutines
) )
@@ -40,7 +39,7 @@ func matchArguments(args []string) string {
"\nFor help (this output): gopass -h\n" + "\nFor help (this output): gopass -h\n" +
"For version: gopass -v\n" "For version: gopass -v\n"
default: default:
err := error(nil) var err error
size, err = strconv.Atoi(args[0]) size, err = strconv.Atoi(args[0])
if err != nil { if err != nil {
return "Invalid first argument (\"" + args[0] + "\") supplied! (Type gopass -h for help)" return "Invalid first argument (\"" + args[0] + "\") supplied! (Type gopass -h for help)"
@@ -95,21 +94,52 @@ func generatePassword(size int) string {
numWorkers := runtime.NumCPU() numWorkers := runtime.NumCPU()
// Launch the worker goroutines // Launch the worker goroutines
for i := 0; i < numWorkers; i++ { for range numWorkers {
go func() { go func() {
allowedLen := len(allowedCharacters) allowedLen := len(allowedCharacters)
// Calculate the rejection limit to avoid modulo bias.
// Any random byte value > maxByte must be discarded.
maxByte := 255 - (256 % allowedLen)
// Create a buffer for random bytes.
// We read enough bytes for the whole chunk at once (plus some extra
// in case of rejections) to reduce system calls.
randBuf := make([]byte, chunkSize*2)
for { for {
// Generate a chunk of the password // Fill the buffer initially
if _, err := rand.Read(randBuf); err != nil {
println("Error securely generating random character chunk!")
return
}
bufIdx := 0
chunk := make([]rune, chunkSize) chunk := make([]rune, chunkSize)
for i := range chunk { for i := range chunk {
index, err := rand.Int(rand.Reader, big.NewInt(int64(allowedLen))) for {
if err != nil { // If we exhausted the buffer due to rejections, refill it
println("Error securely generating random character chunk!") if bufIdx >= len(randBuf) {
return if _, err := rand.Read(randBuf); err != nil {
} println("Error securely generating random character chunk!")
chunk[i] = allowedCharacters[index.Int64()] return
} }
bufIdx = 0
}
b := randBuf[bufIdx]
bufIdx++
// If the byte causes bias, discard and retry
if int(b) > maxByte {
continue
}
// Safe to map to character set
chunk[i] = allowedCharacters[int(b)%allowedLen]
break
}
}
// Send the chunk of the password to the main goroutine // Send the chunk of the password to the main goroutine
passChan <- chunk passChan <- chunk
} }
+14 -14
View File
@@ -1,5 +1,5 @@
# The current version number of the program # The current version number of the program
VERSION := 1.3.4 VERSION := 1.3.5
# List of OS and architecture combinations to build # List of OS and architecture combinations to build
BUILD_OS_ARCH := \ BUILD_OS_ARCH := \
@@ -24,64 +24,64 @@ all: $(BUILD_OS_ARCH)
darwin/amd64: darwin/amd64:
GOOS=darwin \ GOOS=darwin \
GOARCH=amd64 \ GOARCH=amd64 \
go build -ldflags="-s -w" -o "gopass-darwin-amd64-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-darwin-amd64-$(VERSION)" main.go
freebsd/386: freebsd/386:
GOOS=freebsd \ GOOS=freebsd \
GOARCH=386 \ GOARCH=386 \
go build -ldflags="-s -w" -o "gopass-freebsd-386-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-freebsd-386-$(VERSION)" main.go
freebsd/amd64: freebsd/amd64:
GOOS=freebsd \ GOOS=freebsd \
GOARCH=amd64 \ GOARCH=amd64 \
go build -ldflags="-s -w" -o "gopass-freebsd-amd64-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-freebsd-amd64-$(VERSION)" main.go
freebsd/arm: freebsd/arm:
GOOS=freebsd \ GOOS=freebsd \
GOARCH=arm \ GOARCH=arm \
go build -ldflags="-s -w" -o "gopass-freebsd-arm-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-freebsd-arm-$(VERSION)" main.go
linux/386: linux/386:
GOOS=linux \ GOOS=linux \
GOARCH=386 \ GOARCH=386 \
go build -ldflags="-s -w" -o "gopass-linux-386-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-linux-386-$(VERSION)" main.go
linux/amd64: linux/amd64:
GOOS=linux \ GOOS=linux \
GOARCH=amd64 \ GOARCH=amd64 \
go build -ldflags="-s -w" -o "gopass-linux-amd64-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-linux-amd64-$(VERSION)" main.go
linux/arm: linux/arm:
GOOS=linux \ GOOS=linux \
GOARCH=arm \ GOARCH=arm \
go build -ldflags="-s -w" -o "gopass-linux-arm-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-linux-arm-$(VERSION)" main.go
plan9/386: plan9/386:
GOOS=plan9 \ GOOS=plan9 \
GOARCH=386 \ GOARCH=386 \
go build -ldflags="-s -w" -o "gopass-plan9-386-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-plan9-386-$(VERSION)" main.go
plan9/amd64: plan9/amd64:
GOOS=plan9 \ GOOS=plan9 \
GOARCH=amd64 \ GOARCH=amd64 \
go build -ldflags="-s -w" -o "gopass-plan9-amd64-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-plan9-amd64-$(VERSION)" main.go
plan9/arm: plan9/arm:
GOOS=plan9 \ GOOS=plan9 \
GOARCH=arm \ GOARCH=arm \
go build -ldflags="-s -w" -o "gopass-plan9-arm-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-plan9-arm-$(VERSION)" main.go
windows/386: windows/386:
GOOS=windows \ GOOS=windows \
GOARCH=386 \ GOARCH=386 \
go build -ldflags="-s -w" -o "gopass-windows-386-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-windows-386-$(VERSION)" main.go
windows/amd64: windows/amd64:
GOOS=windows \ GOOS=windows \
GOARCH=amd64 \ GOARCH=amd64 \
go build -ldflags="-s -w" -o "gopass-windows-amd64-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-windows-amd64-$(VERSION)" main.go
windows/arm: windows/arm:
GOOS=windows \ GOOS=windows \
GOARCH=arm \ GOARCH=arm \
go build -ldflags="-s -w" -o "gopass-windows-arm-$(VERSION)" main.go go build -trimpath -buildvcs=false -ldflags="-s -w" -o "gopass-windows-arm-$(VERSION)" main.go