1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
// This is initial implementation of CTR_DRBG with AES-256. Code is tested
// and functionaly correct. Nevertheless it will be changed
//
// TODO: Following things still need to be done
// * Add other AES key lengts
// * Validate sizes from table 3 of SP800-90A
// * Improve reseeding so that code returns an error when reseed is needed
// * Add case with derivation function (maybe)
// * Code cleanup
// * Implement benchmark
// * Add rest of the test vectors from CAVP
package drbg
import (
"github.com/henrydcase/nobs/drbg/internal/aes"
"github.com/henrydcase/nobs/utils"
)
// Constants below correspond to AES-256, which is currently
// the only block cipher supported.
const (
BlockLen = 16
KeyLen = 32
SeedLen = BlockLen + KeyLen
)
type CtrDrbg struct {
v [BlockLen]byte
key [KeyLen]byte
counter uint
strength uint
resistance bool
blockEnc aes.IAES
tmpBlk [3 * BlockLen]byte
}
func NewCtrDrbg() *CtrDrbg {
if utils.X86.HasAES {
return &CtrDrbg{blockEnc: &aes.AESAsm{}}
}
return &CtrDrbg{blockEnc: &aes.AES{}}
}
func (c *CtrDrbg) inc() {
for i := BlockLen - 1; i >= 0; i-- {
if c.v[i] == 0xff {
c.v[i] = 0x00
} else {
c.v[i]++
break
}
}
}
func (c *CtrDrbg) Init(entropy, personalization []byte) bool {
var lsz int
var seedBuf [SeedLen]byte
// Minimum entropy input (SP800-90A, 10.2.1)
if len(entropy) < int(c.strength/8) {
return false
}
// Security strength for AES-256 as per SP800-57, 5.6.1
c.strength = 256
lsz = len(entropy)
if lsz > SeedLen {
lsz = SeedLen
}
copy(seedBuf[:], entropy[:lsz])
lsz = len(personalization)
if lsz > SeedLen {
lsz = SeedLen
}
for i := 0; i < lsz; i++ {
seedBuf[i] ^= personalization[i]
}
c.blockEnc.SetKey(c.key[:])
c.update(seedBuf[:])
c.counter = 1
return true
}
func (c *CtrDrbg) update(data []byte) {
if len(data) != SeedLen {
panic("Provided data is not equal to strength/8")
}
// deliberatelly not using len(c.tmpBlk)
for i := 0; i < 3*BlockLen; i += BlockLen {
c.inc()
c.blockEnc.SetKey(c.key[:])
c.blockEnc.Encrypt(c.tmpBlk[i:], c.v[:])
}
for i := 0; i < 3*BlockLen; i++ {
c.tmpBlk[i] ^= data[i]
}
copy(c.key[:], c.tmpBlk[:KeyLen])
copy(c.v[:], c.tmpBlk[KeyLen:])
}
func (c *CtrDrbg) Reseed(entropy, data []byte) {
var seedBuf [SeedLen]byte
var lsz int
lsz = len(entropy)
if lsz > SeedLen {
lsz = SeedLen
}
copy(seedBuf[:], entropy[:lsz])
lsz = len(data)
if lsz > SeedLen {
lsz = SeedLen
}
for i := 0; i < lsz; i++ {
seedBuf[i] ^= data[i]
}
c.update(seedBuf[:])
c.counter = 1
}
func (c *CtrDrbg) ReadWithAdditionalData(out, ad []byte) (n int, err error) {
var seedBuf [SeedLen]byte
// TODO: check reseed_counter > reseed_interval
if len(ad) > 0 {
// pad additional data with zeros if needed
copy(seedBuf[:], ad)
c.update(seedBuf[:])
}
// Number of blocks to write minus last one
blocks := len(out) / BlockLen
for i := 0; i < blocks; i++ {
c.inc()
c.blockEnc.SetKey(c.key[:])
c.blockEnc.Encrypt(out[i*BlockLen:], c.v[:])
}
// Copy remainder - case for out being not block aligned
c.blockEnc.Encrypt(c.tmpBlk[:], c.v[:])
copy(out[blocks*BlockLen:], c.tmpBlk[:len(out)%BlockLen])
c.update(seedBuf[:])
c.counter += 1
return len(out), nil
}
// Read reads data from DRBG. Size of data is determined by
// out buffer.
func (c *CtrDrbg) Read(out []byte) (n int, err error) {
return c.ReadWithAdditionalData(out, nil)
}
|