|
- // 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
- // * 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)
- }
|