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 package argon2
36
37 import (
38 "encoding/binary"
39 "sync"
40
41 "golang.org/x/crypto/blake2b"
42 )
43
44
45 const Version = 0x13
46
47 const (
48 argon2d = iota
49 argon2i
50 argon2id
51 )
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72 func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
73 return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
74 }
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96 func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
97 return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
98 }
99
100 func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
101 if time < 1 {
102 panic("argon2: number of rounds too small")
103 }
104 if threads < 1 {
105 panic("argon2: parallelism degree too low")
106 }
107 h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode)
108
109 memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads))
110 if memory < 2*syncPoints*uint32(threads) {
111 memory = 2 * syncPoints * uint32(threads)
112 }
113 B := initBlocks(&h0, memory, uint32(threads))
114 processBlocks(B, time, memory, uint32(threads), mode)
115 return extractKey(B, memory, uint32(threads), keyLen)
116 }
117
118 const (
119 blockLength = 128
120 syncPoints = 4
121 )
122
123 type block [blockLength]uint64
124
125 func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte {
126 var (
127 h0 [blake2b.Size + 8]byte
128 params [24]byte
129 tmp [4]byte
130 )
131
132 b2, _ := blake2b.New512(nil)
133 binary.LittleEndian.PutUint32(params[0:4], threads)
134 binary.LittleEndian.PutUint32(params[4:8], keyLen)
135 binary.LittleEndian.PutUint32(params[8:12], memory)
136 binary.LittleEndian.PutUint32(params[12:16], time)
137 binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
138 binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
139 b2.Write(params[:])
140 binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
141 b2.Write(tmp[:])
142 b2.Write(password)
143 binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
144 b2.Write(tmp[:])
145 b2.Write(salt)
146 binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
147 b2.Write(tmp[:])
148 b2.Write(key)
149 binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
150 b2.Write(tmp[:])
151 b2.Write(data)
152 b2.Sum(h0[:0])
153 return h0
154 }
155
156 func initBlocks(h0 *[blake2b.Size + 8]byte, memory, threads uint32) []block {
157 var block0 [1024]byte
158 B := make([]block, memory)
159 for lane := uint32(0); lane < threads; lane++ {
160 j := lane * (memory / threads)
161 binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
162
163 binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
164 blake2bHash(block0[:], h0[:])
165 for i := range B[j+0] {
166 B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
167 }
168
169 binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
170 blake2bHash(block0[:], h0[:])
171 for i := range B[j+1] {
172 B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
173 }
174 }
175 return B
176 }
177
178 func processBlocks(B []block, time, memory, threads uint32, mode int) {
179 lanes := memory / threads
180 segments := lanes / syncPoints
181
182 processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
183 var addresses, in, zero block
184 if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
185 in[0] = uint64(n)
186 in[1] = uint64(lane)
187 in[2] = uint64(slice)
188 in[3] = uint64(memory)
189 in[4] = uint64(time)
190 in[5] = uint64(mode)
191 }
192
193 index := uint32(0)
194 if n == 0 && slice == 0 {
195 index = 2
196 if mode == argon2i || mode == argon2id {
197 in[6]++
198 processBlock(&addresses, &in, &zero)
199 processBlock(&addresses, &addresses, &zero)
200 }
201 }
202
203 offset := lane*lanes + slice*segments + index
204 var random uint64
205 for index < segments {
206 prev := offset - 1
207 if index == 0 && slice == 0 {
208 prev += lanes
209 }
210 if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
211 if index%blockLength == 0 {
212 in[6]++
213 processBlock(&addresses, &in, &zero)
214 processBlock(&addresses, &addresses, &zero)
215 }
216 random = addresses[index%blockLength]
217 } else {
218 random = B[prev][0]
219 }
220 newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
221 processBlockXOR(&B[offset], &B[prev], &B[newOffset])
222 index, offset = index+1, offset+1
223 }
224 wg.Done()
225 }
226
227 for n := uint32(0); n < time; n++ {
228 for slice := uint32(0); slice < syncPoints; slice++ {
229 var wg sync.WaitGroup
230 for lane := uint32(0); lane < threads; lane++ {
231 wg.Add(1)
232 go processSegment(n, slice, lane, &wg)
233 }
234 wg.Wait()
235 }
236 }
237
238 }
239
240 func extractKey(B []block, memory, threads, keyLen uint32) []byte {
241 lanes := memory / threads
242 for lane := uint32(0); lane < threads-1; lane++ {
243 for i, v := range B[(lane*lanes)+lanes-1] {
244 B[memory-1][i] ^= v
245 }
246 }
247
248 var block [1024]byte
249 for i, v := range B[memory-1] {
250 binary.LittleEndian.PutUint64(block[i*8:], v)
251 }
252 key := make([]byte, keyLen)
253 blake2bHash(key, block[:])
254 return key
255 }
256
257 func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
258 refLane := uint32(rand>>32) % threads
259 if n == 0 && slice == 0 {
260 refLane = lane
261 }
262 m, s := 3*segments, ((slice+1)%syncPoints)*segments
263 if lane == refLane {
264 m += index
265 }
266 if n == 0 {
267 m, s = slice*segments, 0
268 if slice == 0 || lane == refLane {
269 m += index
270 }
271 }
272 if index == 0 || lane == refLane {
273 m--
274 }
275 return phi(rand, uint64(m), uint64(s), refLane, lanes)
276 }
277
278 func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
279 p := rand & 0xFFFFFFFF
280 p = (p * p) >> 32
281 p = (p * m) >> 32
282 return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
283 }
284
View as plain text