package hmacdrbg

//Ported from: https://github.com/fpgaminer/python-hmac-drbg
import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/binary"
	"errors"
	"time"
)

/**937 bytes (~7500 bits) as per the spec.*/
const MaxBytesPerGenerate = 937  // ~ 7500bits/8

/**Entropy for NewHmacDrbg() and Reseed() must never exceed this number of bytes.*/
const MaxEntropyBytes = 125      // = 1000bits

const MaxPersonalizationBytes = 32 // = 256bits

type HmacDrbg struct {
	/**The effective security level (eg 128 bits) which this generator was instantiated with.*/
	SecurityLevelBits int
	
	k, v []byte
	
	reseedCounter int
	updateCounter []byte
}

/**Read from an arbitrary number of bytes from HmacDrbg efficiently.
Internally it generates blocks of MaxBytesPerGenerate.  It then
serves these out through the standard `Read` function.  Read returns
an error if reseed becomes is necessary.
*/
type HmacDrbgReader struct {
	Drbg *HmacDrbg
	buffer []byte //size MaxBytesPerGenerate
	offset int
}

/**Create a new DRBG.
desiredSecurityLevelBits must be one of 112, 128, 192, 256.

entropy length (in bits) must be at least 1.5 times securityLevelBits.
entropy byte length cannot exceed MaxEntropyBytes.

The personalization can be nil.  If non-nil, it's byte length cannot exceed MaxPersonalizationBytes.

If any of the parameters are out-of-range this function will panic.
*/
func NewHmacDrbg(securityLevelBits int, entropy, personalization []byte) *HmacDrbg {
	if securityLevelBits != 112 && 
		securityLevelBits != 128 &&
		securityLevelBits != 192 &&
		securityLevelBits != 256 {
		
		panic("Illegal desiredSecurityLevelBits")
	}
	
	if len(entropy) > MaxEntropyBytes {
		panic("Input entropy too large")
	}
	
	if (len(entropy) * 8 * 2) < (securityLevelBits * 3) {
		panic("Insufficient entropy for security level")
	}
	
	if personalization != nil && len(personalization) > MaxPersonalizationBytes {
		panic("Personalization too long")
	}
	
	self := &HmacDrbg{
		SecurityLevelBits: securityLevelBits,
		k: make([]byte, 32),
		v: make([]byte, 32),
		reseedCounter: 1,
		updateCounter: make([]byte,8),
	}
	binary.LittleEndian.PutUint64(self.updateCounter, uint64(time.Now().Unix()))
	
	//Instantiate
	//k already holds 0x00.
	//Fill v with 0x01.
	for i := range self.v {
		self.v[i] = 0x01
	}
	
	nPers := 0
	if personalization != nil {
		nPers = len(personalization)
	}
	seed := make([]byte, len(entropy) + nPers)
	copy(seed, entropy)
	if personalization != nil {
		copy(seed[len(entropy):], personalization)
	}
	
	self.update(seed)
	
	return self
}

func (self *HmacDrbg) _hmac(key, message []byte) []byte {
	hm := hmac.New(sha256.New, key)
	hm.Write(message)
	return hm.Sum(nil)
}

func (self *HmacDrbg) update(providedData []byte) {
	nProvided := 0
	if providedData != nil {
		nProvided = len(providedData)
	}		

	msg := make([]byte, len(self.v) + 1 + nProvided)
	copy(msg, self.v)
	//leave hole with 0x00 at msg[len(self.v)]
	if (providedData != nil) {
		copy(msg[len(self.v)+1:], providedData)
	}

	self.k = self._hmac(self.k, msg)
	self.v = self._hmac(self.k, self.v)

	if providedData != nil {
		copy(msg, self.v)
		msg[len(self.v)] = 0x01
		copy(msg[len(self.v)+1:], providedData)
		
		self.k = self._hmac(self.k, msg)
		self.v = self._hmac(self.k, self.v)
	}

	for i := len(self.updateCounter)-1; i>=0 ;i-- {
		self.updateCounter[i]++
		if self.updateCounter[i] != 0 {
			break
		}
	}
}

func (self *HmacDrbg) Reseed(entropy []byte) error {
	if len(entropy) * 8 < self.SecurityLevelBits {
		return errors.New("Reseed entropy is less than security-level")
	}
	
	if len(entropy) > MaxEntropyBytes {
		return errors.New("Reseed entropy exceeds MaxEntropyBytes")
	}
	
	self.update(entropy)
	self.reseedCounter = 1
	
	return nil
}

/**Fill the given byte array with random bytes.
Returns false if a reseed is necessary first.
This function will panic if the array is larger than MaxBytesPerGenerate.*/
func (self *HmacDrbg) Generate(outputBytes []byte) bool {
	nWanted := len(outputBytes)
	if nWanted > MaxBytesPerGenerate {
		panic("HmacDrbg: generate request too large.")
	}
	
	if self.reseedCounter >= 10000 {
		//set all bytes to zero, just to be clear
		for i := range outputBytes {
			outputBytes[i] = 0
		}
		return false
	}

	nGen := 0
	var n int
	for nGen < nWanted {
		self.v = self._hmac(self.k, self.v)
		
		n = nWanted - nGen
		if n > len(self.v) {
			n = len(self.v)
		}
		copy(outputBytes[nGen:], self.v[0:n])
		nGen += n
	}

	// Provide an incrementing additional-input when generating, to provide backtracking resistance.  C.f.
	// https://link.springer.com/chapter/10.1007%2F978-3-030-17656-3_6
	// The value isn't secret, but just must vary.
	self.update(self.updateCounter)
	self.reseedCounter++
	
	return true
} 

func NewHmacDrbgReader(drbg *HmacDrbg) *HmacDrbgReader {
	return &HmacDrbgReader{
		Drbg: drbg,
		buffer: make([]byte, MaxBytesPerGenerate),
		offset: MaxBytesPerGenerate,
	}
}

func (self *HmacDrbgReader) Read(b []byte) (n int, err error) {
	nRead := 0
	nWanted := len(b)
	for nRead < nWanted {
		if self.offset >= MaxBytesPerGenerate {
			if !self.Drbg.Generate(self.buffer) {
				return nRead, errors.New("MUST_RESEED")
			}
			self.offset = 0
		}
		
		b[nRead] = self.buffer[self.offset]
		nRead++
		self.offset++
	}
	
	return nRead, nil
}
