2023-01-15 16:14:19 +01:00
|
|
|
// SPDX-FileCopyrightText: 2022-2023 The go-mail Authors
|
2022-10-26 14:05:25 +02:00
|
|
|
//
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
package mail
|
|
|
|
|
|
|
|
import (
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/binary"
|
|
|
|
"fmt"
|
|
|
|
"math/big"
|
|
|
|
"strings"
|
|
|
|
)
|
|
|
|
|
|
|
|
// Range of characters for the secure string generation
|
|
|
|
const cr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890"
|
|
|
|
|
|
|
|
// Bitmask sizes for the string generators (based on 93 chars total)
|
|
|
|
const (
|
|
|
|
letterIdxBits = 7 // 7 bits to represent a letter index
|
|
|
|
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
|
|
|
letterIdxMax = 63 / letterIdxBits // # of letter indices fitting in 63 bits
|
|
|
|
)
|
|
|
|
|
2024-02-27 11:30:09 +01:00
|
|
|
// randomStringSecure returns a random, string of length characters. This method uses the
|
2022-10-26 14:05:25 +02:00
|
|
|
// crypto/random package and therfore is cryptographically secure
|
2024-02-27 11:30:09 +01:00
|
|
|
func randomStringSecure(length int) (string, error) {
|
|
|
|
randString := strings.Builder{}
|
|
|
|
randString.Grow(length)
|
|
|
|
charRangeLength := len(cr)
|
2022-10-26 14:05:25 +02:00
|
|
|
|
2024-02-27 11:30:09 +01:00
|
|
|
randPool := make([]byte, 8)
|
|
|
|
_, err := rand.Read(randPool)
|
2022-10-26 14:05:25 +02:00
|
|
|
if err != nil {
|
2024-02-27 11:30:09 +01:00
|
|
|
return randString.String(), err
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
for idx, char, rest := length-1, binary.BigEndian.Uint64(randPool), letterIdxMax; idx >= 0; {
|
|
|
|
if rest == 0 {
|
|
|
|
_, err = rand.Read(randPool)
|
2022-10-26 14:05:25 +02:00
|
|
|
if err != nil {
|
2024-02-27 11:30:09 +01:00
|
|
|
return randString.String(), err
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
char, rest = binary.BigEndian.Uint64(randPool), letterIdxMax
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
if idx := int(char & letterIdxMask); idx < charRangeLength {
|
|
|
|
randString.WriteByte(cr[idx])
|
|
|
|
idx--
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
char >>= letterIdxBits
|
|
|
|
rest--
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
|
|
|
|
2024-02-27 11:30:09 +01:00
|
|
|
return randString.String(), nil
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
|
|
|
|
2024-02-27 11:30:09 +01:00
|
|
|
// randNum returns a random number with a maximum value of length
|
|
|
|
func randNum(length int) (int, error) {
|
|
|
|
if length <= 0 {
|
|
|
|
return 0, fmt.Errorf("provided number is <= 0: %d", length)
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
length64 := big.NewInt(int64(length))
|
|
|
|
if !length64.IsUint64() {
|
|
|
|
return 0, fmt.Errorf("big.NewInt() generation returned negative value: %d", length64)
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
randNum64, err := rand.Int(rand.Reader, length64)
|
2022-10-26 14:05:25 +02:00
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
randomNum := int(randNum64.Int64())
|
|
|
|
if randomNum < 0 {
|
|
|
|
return 0, fmt.Errorf("generated random number does not fit as int64: %d", randNum64)
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|
2024-02-27 11:30:09 +01:00
|
|
|
return randomNum, nil
|
2022-10-26 14:05:25 +02:00
|
|
|
}
|