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) }