File: ctr_drbg.go

package info (click to toggle)
golang-github-henrydcase-nobs 0.1%2Bgit20200305.7d891c7-6
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 2,928 kB
  • sloc: asm: 6,587; makefile: 53; python: 38
file content (163 lines) | stat: -rw-r--r-- 3,442 bytes parent folder | download | duplicates (2)
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)
}