Files
hotp_calc/internal/store/generate.go
T

63 lines
2.1 KiB
Go

package store
import (
"crypto/rand"
"encoding/hex"
"fmt"
"strings"
)
const (
// SerialLength — длина серийного номера eToken PASS в символах.
SerialLength = 14
// DefaultSerialPrefix — префикс серийных номеров eToken PASS.
DefaultSerialPrefix = "000400"
// SeedBytes — длина вектора инициализации в байтах.
SeedBytes = 20
)
// NewSeed генерирует криптографически стойкий вектор инициализации.
func NewSeed(n int) ([]byte, error) {
if n <= 0 {
n = SeedBytes
}
b := make([]byte, n)
if _, err := rand.Read(b); err != nil {
return nil, fmt.Errorf("не удалось получить случайные данные: %w", err)
}
return b, nil
}
// NewSerial генерирует серийный номер: заданный префикс и случайный
// шестнадцатеричный хвост до общей длины SerialLength.
func NewSerial(prefix string) (string, error) {
prefix = strings.ToUpper(strings.TrimSpace(prefix))
if err := ValidateSerial(prefix + "0"); err != nil {
return "", fmt.Errorf("некорректный префикс серийного номера: %w", err)
}
suffix := SerialLength - len(prefix)
if suffix < 4 {
suffix = 4
}
b := make([]byte, (suffix+1)/2)
if _, err := rand.Read(b); err != nil {
return "", fmt.Errorf("не удалось получить случайные данные: %w", err)
}
return prefix + strings.ToUpper(hex.EncodeToString(b))[:suffix], nil
}
// NewUniqueSerial подбирает серийный номер, которого ещё нет в хранилище.
func (s *Store) NewUniqueSerial(prefix string) (string, error) {
for i := 0; i < 100; i++ {
serial, err := NewSerial(prefix)
if err != nil {
return "", err
}
if s.file.Section(serial) == nil {
return serial, nil
}
}
return "", fmt.Errorf("не удалось подобрать свободный серийный номер с префиксом %q", prefix)
}