A Go implementation of the Subset Counter-Based Deterministic Random Bit Generator (SC_DRBG).
This is a new and experimental function that has not undergone any review or audit. In the absence of cryptanalysis, use at your own risk.
Provides a deterministic random bit generator that maintains an array of seed material in its internal state (rather than a single seed), allowing each output to be generated from a configurable subset of array elements.
SC_DRBG supports 32 and 64 bit unsigned integers for the internal counter and other integer values. The byte order for integer encoding and decoding is configurable via different constructors, and an Endian enum. Supports commitment of elements from an array of seed material to their positions, lengths, and contents, and configurable rounds of mixing for entropy diffusion across elements. Forward secrecy is provided through continuous state evolution.
The Drbg structure representing SC_DRBG is compatible with rand/v2 as a Source. One generic parameter T is expected, as the type for all integer values (including the internal counter) as either uint32 or uint64.
An instance of Drbg can be initialized via four different constructors, differentiated by integer type and endianness:
- NewUint32LE: Uses 32 bit integers, with little-endian byte order.
- NewUint32BE: Uses 32 bit integers, with big-endian byte order.
- NewUint64LE: Uses 64 bit integers, with little-endian byte order.
- NewUint64BE: Uses 64 bit integers, with big-endian byte order.
All constructors create a new instance from an array of seed material, and an optional context string. A boolean value indicating if the seed material should be processed by an initialization function is also expected. When true, the seed material will undergo initialization1 before the new instance is created, and when false, the seed material will directly be used in the new instance.
The initialization function Init for arrays of seed material completes two steps to process seed material before creating a new Drbg instance: Committing elements to their properties, and mixing for entropy diffusion. The function expects an array of seed material, optional context string, nonce, number of mixing rounds, and an Endian enum for byte order. In the first step, for each element in the array of seed material, an HMAC is created to commit each element to its position, length, and content. In the second step, the committed elements undergo rounds of mixing to diffuse entropy across elements, using a SHAKE256 sponge "absorb then squeeze" construction.
The Drbg generator methods are used to produce random numbers and random bytes. The generator provides forward secrecy by updating the state after each call. One round of mixing is applied, using the last generated bytes in deriving the mixing key, and a new pseudorandom key for the next round is derived. Two variants of each method are supported, one that uses all elements from the array of seed material to seed the generator, and one using a subset of elements to seed the generator, with the subset clamped to the total array length:
- FillBytesSubset: Fills a destination buffer with random bytes, seeded by a subset of elements.
- FillBytes: Fills a destination buffer with random bytes, seeded by all elements.
- Uint32Subset: Returns the next random unsigned 32 bit integer, seeded by a subset of elements.
- Uint32: Returns the next random unsigned 32 bit integer, seeded by all elements.
- Uint64Subset: Returns the next random unsigned 64 bit integer, seeded by a subset of elements.
- Uint64: Returns the next random unsigned 64 bit integer, seeded by all elements.
package main
import (
"crypto/sha3"
"encoding/hex"
"hash"
"github.com/jacobhaap/go-sc_drbg"
)
func main() {
seeds := []string{
"456E64204F662054686520576F726C642053756E",
"556E6D616B65207468652057696C64204C69676874",
"536166652050617373616765",
"747261636B6572706C61747A",
"3635646179736F66737461746963",
}
arr := make([][]byte, 5)
for i := range seeds {
arr[i], _ = hex.DecodeString(seeds[i])
}
context := "some-random-application"
h := func() hash.Hash { return sha3.New256() }
drbg, _ := sc_drbg.NewUint32LE(h, arr, context, true)
num := drbg.Uint32()
if num != 4076030162 {
panic("generator produced unexpected uint32")
}
}Footnotes
-
See the Initialization Function section for more details. When an array of seed material undergoes initialization via one of the constructors, the nonce is derived from a hash of the seed material, and one round of mixing is applied. ↩