Source file src/crypto/internal/fips140/ecdsa/hmacdrbg.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 ecdsa
     6  
     7  import (
     8  	"bytes"
     9  	"crypto/internal/fips140"
    10  	"crypto/internal/fips140/hmac"
    11  )
    12  
    13  // hmacDRBG is an SP 800-90A Rev. 1 HMAC_DRBG.
    14  //
    15  // It is only intended to be used to generate ECDSA nonces. Since it will be
    16  // instantiated ex-novo for each signature, its Generate function will only be
    17  // invoked once or twice (only for P-256, with probability 2⁻³²).
    18  //
    19  // Per Table 2, it has a reseed interval of 2^48 requests, and a maximum request
    20  // size of 2^19 bits (2^16 bytes, 64 KiB).
    21  type hmacDRBG struct {
    22  	newHMAC func(key []byte) *hmac.HMAC
    23  
    24  	hK *hmac.HMAC
    25  	V  []byte
    26  
    27  	reseedCounter uint64
    28  }
    29  
    30  const (
    31  	reseedInterval = 1 << 48
    32  	maxRequestSize = (1 << 19) / 8
    33  )
    34  
    35  // plainPersonalizationString is used by HMAC_DRBG as-is.
    36  type plainPersonalizationString []byte
    37  
    38  func (plainPersonalizationString) isPersonalizationString() {}
    39  
    40  // Each entry in blockAlignedPersonalizationString is written to the HMAC at a
    41  // block boundary, as specified in draft-irtf-cfrg-det-sigs-with-noise-04,
    42  // Section 4.
    43  type blockAlignedPersonalizationString [][]byte
    44  
    45  func (blockAlignedPersonalizationString) isPersonalizationString() {}
    46  
    47  type personalizationString interface {
    48  	isPersonalizationString()
    49  }
    50  
    51  func newDRBG[H fips140.Hash](hash func() H, entropy, nonce []byte, s personalizationString) *hmacDRBG {
    52  	// HMAC_DRBG_Instantiate_algorithm, per Section 10.1.2.3.
    53  	fips140.RecordApproved()
    54  
    55  	d := &hmacDRBG{
    56  		newHMAC: func(key []byte) *hmac.HMAC {
    57  			return hmac.New(hash, key)
    58  		},
    59  	}
    60  	size := hash().Size()
    61  
    62  	// K = 0x00 0x00 0x00 ... 0x00
    63  	K := make([]byte, size)
    64  
    65  	// V = 0x01 0x01 0x01 ... 0x01
    66  	d.V = bytes.Repeat([]byte{0x01}, size)
    67  
    68  	// HMAC_DRBG_Update, per Section 10.1.2.2.
    69  	// K = HMAC (K, V || 0x00 || provided_data)
    70  	h := hmac.New(hash, K)
    71  	h.Write(d.V)
    72  	h.Write([]byte{0x00})
    73  	h.Write(entropy)
    74  	h.Write(nonce)
    75  	switch s := s.(type) {
    76  	case plainPersonalizationString:
    77  		h.Write(s)
    78  	case blockAlignedPersonalizationString:
    79  		l := len(d.V) + 1 + len(entropy) + len(nonce)
    80  		for _, b := range s {
    81  			pad000(h, l)
    82  			h.Write(b)
    83  			l = len(b)
    84  		}
    85  	}
    86  	K = h.Sum(K[:0])
    87  	// V = HMAC (K, V)
    88  	h = hmac.New(hash, K)
    89  	h.Write(d.V)
    90  	d.V = h.Sum(d.V[:0])
    91  	// K = HMAC (K, V || 0x01 || provided_data).
    92  	h.Reset()
    93  	h.Write(d.V)
    94  	h.Write([]byte{0x01})
    95  	h.Write(entropy)
    96  	h.Write(nonce)
    97  	switch s := s.(type) {
    98  	case plainPersonalizationString:
    99  		h.Write(s)
   100  	case blockAlignedPersonalizationString:
   101  		l := len(d.V) + 1 + len(entropy) + len(nonce)
   102  		for _, b := range s {
   103  			pad000(h, l)
   104  			h.Write(b)
   105  			l = len(b)
   106  		}
   107  	}
   108  	K = h.Sum(K[:0])
   109  	// V = HMAC (K, V)
   110  	h = hmac.New(hash, K)
   111  	h.Write(d.V)
   112  	d.V = h.Sum(d.V[:0])
   113  
   114  	d.hK = h
   115  	d.reseedCounter = 1
   116  	return d
   117  }
   118  
   119  func pad000(h *hmac.HMAC, writtenSoFar int) {
   120  	blockSize := h.BlockSize()
   121  	if rem := writtenSoFar % blockSize; rem != 0 {
   122  		h.Write(make([]byte, blockSize-rem))
   123  	}
   124  }
   125  
   126  // Generate produces at most maxRequestSize bytes of random data in out.
   127  func (d *hmacDRBG) Generate(out []byte) {
   128  	// HMAC_DRBG_Generate_algorithm, per Section 10.1.2.5.
   129  	fips140.RecordApproved()
   130  
   131  	if len(out) > maxRequestSize {
   132  		panic("ecdsa: internal error: request size exceeds maximum")
   133  	}
   134  
   135  	if d.reseedCounter > reseedInterval {
   136  		panic("ecdsa: reseed interval exceeded")
   137  	}
   138  
   139  	tlen := 0
   140  	for tlen < len(out) {
   141  		// V = HMAC_K(V)
   142  		// T = T || V
   143  		d.hK.Reset()
   144  		d.hK.Write(d.V)
   145  		d.V = d.hK.Sum(d.V[:0])
   146  		tlen += copy(out[tlen:], d.V)
   147  	}
   148  
   149  	// Note that if this function shows up on ECDSA-level profiles, this can be
   150  	// optimized in the common case by deferring the rest to the next Generate
   151  	// call, which will never come in nearly all cases.
   152  
   153  	// HMAC_DRBG_Update, per Section 10.1.2.2, without provided_data.
   154  	// K = HMAC (K, V || 0x00)
   155  	d.hK.Reset()
   156  	d.hK.Write(d.V)
   157  	d.hK.Write([]byte{0x00})
   158  	K := d.hK.Sum(nil)
   159  	// V = HMAC (K, V)
   160  	d.hK = d.newHMAC(K)
   161  	d.hK.Write(d.V)
   162  	d.V = d.hK.Sum(d.V[:0])
   163  
   164  	d.reseedCounter++
   165  }
   166  

View as plain text