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  // TestingOnlyNewDRBG creates an SP 800-90A Rev. 1 HMAC_DRBG with a plain
   120  // personalization string.
   121  //
   122  // This should only be used for ACVP testing. hmacDRBG is not intended to be
   123  // used directly.
   124  func TestingOnlyNewDRBG(hash func() fips140.Hash, entropy, nonce []byte, s []byte) *hmacDRBG {
   125  	return newDRBG(hash, entropy, nonce, plainPersonalizationString(s))
   126  }
   127  
   128  func pad000(h *hmac.HMAC, writtenSoFar int) {
   129  	blockSize := h.BlockSize()
   130  	if rem := writtenSoFar % blockSize; rem != 0 {
   131  		h.Write(make([]byte, blockSize-rem))
   132  	}
   133  }
   134  
   135  // Generate produces at most maxRequestSize bytes of random data in out.
   136  func (d *hmacDRBG) Generate(out []byte) {
   137  	// HMAC_DRBG_Generate_algorithm, per Section 10.1.2.5.
   138  	fips140.RecordApproved()
   139  
   140  	if len(out) > maxRequestSize {
   141  		panic("ecdsa: internal error: request size exceeds maximum")
   142  	}
   143  
   144  	if d.reseedCounter > reseedInterval {
   145  		panic("ecdsa: reseed interval exceeded")
   146  	}
   147  
   148  	tlen := 0
   149  	for tlen < len(out) {
   150  		// V = HMAC_K(V)
   151  		// T = T || V
   152  		d.hK.Reset()
   153  		d.hK.Write(d.V)
   154  		d.V = d.hK.Sum(d.V[:0])
   155  		tlen += copy(out[tlen:], d.V)
   156  	}
   157  
   158  	// Note that if this function shows up on ECDSA-level profiles, this can be
   159  	// optimized in the common case by deferring the rest to the next Generate
   160  	// call, which will never come in nearly all cases.
   161  
   162  	// HMAC_DRBG_Update, per Section 10.1.2.2, without provided_data.
   163  	// K = HMAC (K, V || 0x00)
   164  	d.hK.Reset()
   165  	d.hK.Write(d.V)
   166  	d.hK.Write([]byte{0x00})
   167  	K := d.hK.Sum(nil)
   168  	// V = HMAC (K, V)
   169  	d.hK = d.newHMAC(K)
   170  	d.hK.Write(d.V)
   171  	d.V = d.hK.Sum(d.V[:0])
   172  
   173  	d.reseedCounter++
   174  }
   175  

View as plain text