Source file src/crypto/internal/fips140/drbg/ctrdrbg.go

     1  // Copyright 2024 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package drbg
     6  
     7  import (
     8  	"crypto/internal/fips140"
     9  	"crypto/internal/fips140/aes"
    10  	"crypto/internal/fips140/subtle"
    11  	"crypto/internal/fips140deps/byteorder"
    12  	"math/bits"
    13  )
    14  
    15  // Counter is an SP 800-90A Rev. 1 CTR_DRBG instantiated with AES-256.
    16  //
    17  // Per Table 3, it has a security strength of 256 bits, a seed size of 384 bits,
    18  // a counter length of 128 bits, a reseed interval of 2^48 requests, and a
    19  // maximum request size of 2^19 bits (2^16 bytes, 64 KiB).
    20  //
    21  // We support a narrow range of parameters that fit the needs of our RNG:
    22  // AES-256, no derivation function, no personalization string, no prediction
    23  // resistance, and 384-bit additional input.
    24  //
    25  // WARNING: this type provides tightly scoped support for the DRBG
    26  // functionality we need for FIPS 140-3 _only_. This type _should not_ be used
    27  // outside of the FIPS 140-3 module for any other use.
    28  //
    29  // In particular, as documented, Counter does not support the derivation
    30  // function, or personalization strings which are necessary for safely using
    31  // this DRBG for generic purposes without leaking sensitive values.
    32  type Counter struct {
    33  	// c is instantiated with K as the key and V as the counter.
    34  	c aes.CTR
    35  
    36  	reseedCounter uint64
    37  }
    38  
    39  const (
    40  	keySize        = 256 / 8
    41  	SeedSize       = keySize + aes.BlockSize
    42  	reseedInterval = 1 << 48
    43  	maxRequestSize = (1 << 19) / 8
    44  )
    45  
    46  func NewCounter(entropy *[SeedSize]byte) *Counter {
    47  	// CTR_DRBG_Instantiate_algorithm, per Section 10.2.1.3.1.
    48  	fips140.RecordApproved()
    49  
    50  	K := make([]byte, keySize)
    51  	V := make([]byte, aes.BlockSize)
    52  
    53  	// V starts at 0, but is incremented in CTR_DRBG_Update before each use,
    54  	// unlike AES-CTR where it is incremented after each use.
    55  	V[len(V)-1] = 1
    56  
    57  	cipher, err := aes.New(K)
    58  	if err != nil {
    59  		panic(err)
    60  	}
    61  
    62  	c := &Counter{}
    63  	c.c = *aes.NewCTR(cipher, V)
    64  	c.update(entropy)
    65  	c.reseedCounter = 1
    66  	return c
    67  }
    68  
    69  func (c *Counter) update(seed *[SeedSize]byte) {
    70  	// CTR_DRBG_Update, per Section 10.2.1.2.
    71  
    72  	temp := make([]byte, SeedSize)
    73  	c.c.XORKeyStream(temp, seed[:])
    74  	K := temp[:keySize]
    75  	V := temp[keySize:]
    76  
    77  	// Again, we pre-increment V, like in NewCounter.
    78  	increment((*[aes.BlockSize]byte)(V))
    79  
    80  	cipher, err := aes.New(K)
    81  	if err != nil {
    82  		panic(err)
    83  	}
    84  	c.c = *aes.NewCTR(cipher, V)
    85  }
    86  
    87  func increment(v *[aes.BlockSize]byte) {
    88  	hi := byteorder.BEUint64(v[:8])
    89  	lo := byteorder.BEUint64(v[8:])
    90  	lo, c := bits.Add64(lo, 1, 0)
    91  	hi, _ = bits.Add64(hi, 0, c)
    92  	byteorder.BEPutUint64(v[:8], hi)
    93  	byteorder.BEPutUint64(v[8:], lo)
    94  }
    95  
    96  func (c *Counter) Reseed(entropy, additionalInput *[SeedSize]byte) {
    97  	// CTR_DRBG_Reseed_algorithm, per Section 10.2.1.4.1.
    98  	fips140.RecordApproved()
    99  
   100  	var seed [SeedSize]byte
   101  	subtle.XORBytes(seed[:], entropy[:], additionalInput[:])
   102  	c.update(&seed)
   103  	c.reseedCounter = 1
   104  }
   105  
   106  // Generate produces at most maxRequestSize bytes of random data in out.
   107  func (c *Counter) Generate(out []byte, additionalInput *[SeedSize]byte) (reseedRequired bool) {
   108  	// CTR_DRBG_Generate_algorithm, per Section 10.2.1.5.1.
   109  	fips140.RecordApproved()
   110  
   111  	if len(out) > maxRequestSize {
   112  		panic("crypto/drbg: internal error: request size exceeds maximum")
   113  	}
   114  
   115  	// Step 1.
   116  	if c.reseedCounter > reseedInterval {
   117  		return true
   118  	}
   119  
   120  	// Step 2.
   121  	if additionalInput != nil {
   122  		c.update(additionalInput)
   123  	} else {
   124  		// If the additional input is null, the first CTR_DRBG_Update is
   125  		// skipped, but the additional input is replaced with an all-zero string
   126  		// for the second CTR_DRBG_Update.
   127  		additionalInput = new([SeedSize]byte)
   128  	}
   129  
   130  	// Steps 3-5.
   131  	clear(out)
   132  	c.c.XORKeyStream(out, out)
   133  	aes.RoundToBlock(&c.c)
   134  
   135  	// Step 6.
   136  	c.update(additionalInput)
   137  
   138  	// Step 7.
   139  	c.reseedCounter++
   140  
   141  	// Step 8.
   142  	return false
   143  }
   144  

View as plain text