// Copyright 2024 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package ecdsa

import (
	
	
	
)

// hmacDRBG is an SP 800-90A Rev. 1 HMAC_DRBG.
//
// It is only intended to be used to generate ECDSA nonces. Since it will be
// instantiated ex-novo for each signature, its Generate function will only be
// invoked once or twice (only for P-256, with probability 2⁻³²).
//
// Per Table 2, it has a reseed interval of 2^48 requests, and a maximum request
// size of 2^19 bits (2^16 bytes, 64 KiB).
type hmacDRBG struct {
	newHMAC func(key []byte) *hmac.HMAC

	hK *hmac.HMAC
	V  []byte

	reseedCounter uint64
}

const (
	reseedInterval = 1 << 48
	maxRequestSize = (1 << 19) / 8
)

// plainPersonalizationString is used by HMAC_DRBG as-is.
type plainPersonalizationString []byte

func (plainPersonalizationString) () {}

// Each entry in blockAlignedPersonalizationString is written to the HMAC at a
// block boundary, as specified in draft-irtf-cfrg-det-sigs-with-noise-04,
// Section 4.
type blockAlignedPersonalizationString [][]byte

func (blockAlignedPersonalizationString) () {}

type personalizationString interface {
	isPersonalizationString()
}

func newDRBG[ fips140.Hash]( func() , ,  []byte,  personalizationString) *hmacDRBG {
	// HMAC_DRBG_Instantiate_algorithm, per Section 10.1.2.3.
	fips140.RecordApproved()

	 := &hmacDRBG{
		newHMAC: func( []byte) *hmac.HMAC {
			return hmac.New(, )
		},
	}
	 := ().Size()

	// K = 0x00 0x00 0x00 ... 0x00
	 := make([]byte, )

	// V = 0x01 0x01 0x01 ... 0x01
	.V = bytes.Repeat([]byte{0x01}, )

	// HMAC_DRBG_Update, per Section 10.1.2.2.
	// K = HMAC (K, V || 0x00 || provided_data)
	 := hmac.New(, )
	.Write(.V)
	.Write([]byte{0x00})
	.Write()
	.Write()
	switch s := .(type) {
	case plainPersonalizationString:
		.Write()
	case blockAlignedPersonalizationString:
		 := len(.V) + 1 + len() + len()
		for ,  := range  {
			pad000(, )
			.Write()
			 = len()
		}
	}
	 = .Sum([:0])
	// V = HMAC (K, V)
	 = hmac.New(, )
	.Write(.V)
	.V = .Sum(.V[:0])
	// K = HMAC (K, V || 0x01 || provided_data).
	.Reset()
	.Write(.V)
	.Write([]byte{0x01})
	.Write()
	.Write()
	switch s := .(type) {
	case plainPersonalizationString:
		.Write()
	case blockAlignedPersonalizationString:
		 := len(.V) + 1 + len() + len()
		for ,  := range  {
			pad000(, )
			.Write()
			 = len()
		}
	}
	 = .Sum([:0])
	// V = HMAC (K, V)
	 = hmac.New(, )
	.Write(.V)
	.V = .Sum(.V[:0])

	.hK = 
	.reseedCounter = 1
	return 
}

func pad000( *hmac.HMAC,  int) {
	 := .BlockSize()
	if  :=  % ;  != 0 {
		.Write(make([]byte, -))
	}
}

// Generate produces at most maxRequestSize bytes of random data in out.
func ( *hmacDRBG) ( []byte) {
	// HMAC_DRBG_Generate_algorithm, per Section 10.1.2.5.
	fips140.RecordApproved()

	if len() > maxRequestSize {
		panic("ecdsa: internal error: request size exceeds maximum")
	}

	if .reseedCounter > reseedInterval {
		panic("ecdsa: reseed interval exceeded")
	}

	 := 0
	for  < len() {
		// V = HMAC_K(V)
		// T = T || V
		.hK.Reset()
		.hK.Write(.V)
		.V = .hK.Sum(.V[:0])
		 += copy([:], .V)
	}

	// Note that if this function shows up on ECDSA-level profiles, this can be
	// optimized in the common case by deferring the rest to the next Generate
	// call, which will never come in nearly all cases.

	// HMAC_DRBG_Update, per Section 10.1.2.2, without provided_data.
	// K = HMAC (K, V || 0x00)
	.hK.Reset()
	.hK.Write(.V)
	.hK.Write([]byte{0x00})
	 := .hK.Sum(nil)
	// V = HMAC (K, V)
	.hK = .newHMAC()
	.hK.Write(.V)
	.V = .hK.Sum(.V[:0])

	.reseedCounter++
}