207 lines
4.6 KiB
Go
207 lines
4.6 KiB
Go
package hmacdrbg
|
|
|
|
//Ported from: https://github.com/fpgaminer/python-hmac-drbg
|
|
import (
|
|
"crypto/hmac"
|
|
"crypto/sha256"
|
|
"errors"
|
|
)
|
|
|
|
/**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
|
|
}
|
|
|
|
/**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,
|
|
}
|
|
|
|
//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)
|
|
}
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
self.update(nil)
|
|
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
|
|
}
|