Привет, Хабр!
В версии Go 1.22 пакет math/rand/v2претерпел значительные изменения, а в частности - переход на ChaCha8Rand. Этот новый генератор представляет собой модификацию широко известного и проверенного временем шифра ChaCha8, который используется в протоколах TLS и SSH.
Немного про сам генератор
ChaCha8Rand основывается на алгоритме ChaCha8, который сам по себе является облегченной версией шифра ChaCha20. ChaCha8 выполняет восемь раундов перестановок и замен ключа и блока данных, что гарантирует высокий уровень энтропии и защиту от различных криптографических атак.
ChaCha8Rand принимает 32-байтный ключ, его называют семенем, который используется в качестве начальной точки для генерации случайных чисел. Ключ устанавливает начальное состояние алгоритма, которое затем преобразуется в псевдослучайное состояние для генерации последовательности чисел.
ChaCha8Rand генерирует 64-битные псевдослучайные числа путем прямого вызова алгоритма ChaCha8. Внутри он инициализирует ChaCha8 с заданным ключом и использует блоки данных, которые алгоритм создает при каждом запросе нового случайного значения. Процесс итеративный: каждый вызов генератора ChaCha8Rand изменяет внутреннее состояние и формирует новое случайное число.
ChaCha8Rand поддерживает функции сериализации и десериализации состояния, что позволяет сохранять и восстанавливать текущее состояние генератора.
Реализация ChaCha8Rand в Go
В пакете math/rand/v2, ChaCha8 представлен структурой ChaCha8, которая хранит внутреннее состояние генератора и предоставляет методы для управления им. Состояние инициализируется 32-байтным семенем, передаваемым в функцию NewChaCha8, а сам процесс генерации числа осуществляется методом Uint64(), который возвращает следующее 64-битное псевдослучайное число.
Основные функции и методы, которые используют ChaCha8Rand, включают:
IntN и Int64N: функции генерируют псевдослучайное целое число в заданном диапазоне. Например,
IntN(n int) intвозвращает случайное число от 0 до n-1. Они обеспечивают равномерное распределение значений в указанном диапазоне.Float64: генерирует псевдослучайное число с плавающей точкой между 0.0 и 1.0, не включая 1.0.
Perm: создаёт случайную перестановку чисел от 0 до n-1.
Кроме того, в math/rand/v2 реализованы новые методы MarshalBinary и UnmarshalBinary для объектов, которые реализуют интерфейс Source, включая ChaCha8.
Генератор создается путем вызова NewChaCha8 с 32-байтным массивом начальных значений. Если массив не заполнен случайными данными, генератор не сможет обеспечить криптографическую стойкость.
После инициализации генератора метод Uint64() возвращает следующее псевдослучайное 64-битное число на основе внутреннего состояния. Этот метод использует ChaCha8 для выполнения восемь раундов перестановок и замен.
Примеры
Инициализация генератора и генерация чисел:
package main import ( "fmt" "math/rand/v2" ) func main() { // создаем 32-байтное семя для инициализации генератора seed := [32]byte{'s', 'o', 'm', 'e', 'k', 'e', 'y', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', 'a', 'b', 'c', 'd', 'e', 'f'} // инициализируем новый генератор ChaCha8 generator := rand.NewChaCha8(seed) // получаем несколько случайных чисел fmt.Println(generator.Uint64()) fmt.Println(generator.Uint64()) }
Генерация криптографически безопасного случайного числа:
package main import ( "fmt" "math/rand/v2" ) func main() { seed := [32]byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v'} generator := rand.NewChaCha8(seed) fmt.Println("Secure random number:", generator.Uint64()) }
Генерация случайной строки для токенов или сессий:
package main import ( "fmt" "math/rand/v2" ) func randomBytes(n int) []byte { seed := [32]byte{'s', 'e', 'e', 'd', 'p', 'h', 'r', 'a', 's', 'e', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm'} generator := rand.NewChaCha8(seed) b := make([]byte, n) for i := range b { b[i] = byte(generator.Uint32() % 256) } return b } func main() { fmt.Println("Random session token:", randomBytes(16)) }
Использование в шифровании:
package main import ( "crypto/cipher" "fmt" "golang.org/x/crypto/chacha20" "math/rand/v2" ) func main() { seed := [32]byte{'k', 'e', 'y', 'p', 'h', 'r', 'a', 's', 'e', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n'} generator := rand.NewChaCha8(seed) nonce := [12]byte{} // нормально было бы использовать случайные значения key := [32]byte{} for i := range key { key[i] = byte(generator.Uint32() % 256) } cipher, err := chacha20.NewUnauthenticatedCipher(key[:], nonce[:]) if err != nil { panic(err) } plaintext := []byte("This is a secret message.") ciphertext := make([]byte, len(plaintext)) cipher.XORKeyStream(ciphertext, plaintext) fmt.Println("Encrypted:", ciphertext) fmt.Println("Decrypted message:") cipher.XORKeyStream(ciphertext, ciphertext) // расшифровка производится тем же способом fmt.Println(string(ciphertext)) }
Сравнение ChaCha8Rand с другими генераторами
Составили табличку:
Критерий | ChaCha8Rand | PCG | Rand (Go) |
|---|---|---|---|
Криптографическая безопасность | Высокая (256 бит) | Подходит не для всех задач и в целом низкая | Низкая |
Производительность | Высокая на | Высокая, особенно в микробенчмарках | Средняя |
Универсальность | Подходит для криптографических приложений | Ограниченное использование в криптографии | Не рекомендуется для криптографических приложений |
Методы ускорения | Не требуется | Доступны методы "jump ahead" для быстрого пропуска значений | Отсутствуют |
Размер состояния | 512 бит | 64 или 128 бит | Зависит от реализации |
Ожидается, что сообщество Golang предложит дополнительные стратегии использования этого инструмента, будем следить за новостями!
Больше про языки программирования эксперты OTUS рассказывают в рамках практических онлайн-курсов. С полным каталогом курсов можно ознакомиться по ссылке.
