main
Raw Download raw file
  1// Copyright 2017 The Go Authors. All rights reserved.
  2// Use of this source code is governed by a BSD-style
  3// license that can be found in the LICENSE file.
  4
  5// Package argon2 implements the key derivation function Argon2.
  6// Argon2 was selected as the winner of the Password Hashing Competition and can
  7// be used to derive cryptographic keys from passwords.
  8//
  9// For a detailed specification of Argon2 see [1].
 10//
 11// If you aren't sure which function you need, use Argon2id (IDKey) and
 12// the parameter recommendations for your scenario.
 13//
 14// # Argon2i
 15//
 16// Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
 17// It uses data-independent memory access, which is preferred for password
 18// hashing and password-based key derivation. Argon2i requires more passes over
 19// memory than Argon2id to protect from trade-off attacks. The recommended
 20// parameters (taken from [2]) for non-interactive operations are time=3 and to
 21// use the maximum available memory.
 22//
 23// # Argon2id
 24//
 25// Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
 26// Argon2i and Argon2d. It uses data-independent memory access for the first
 27// half of the first iteration over the memory and data-dependent memory access
 28// for the rest. Argon2id is side-channel resistant and provides better brute-
 29// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
 30// parameters for non-interactive operations (taken from [2]) are time=1 and to
 31// use the maximum available memory.
 32//
 33// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
 34// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
 35package argon2
 36
 37import (
 38	"encoding/binary"
 39	"sync"
 40
 41	"golang.org/x/crypto/blake2b"
 42)
 43
 44// The Argon2 version implemented by this package.
 45const Version = 0x13
 46
 47const (
 48	argon2d = iota
 49	argon2i
 50	argon2id
 51)
 52
 53// Key derives a key from the password, salt, and cost parameters using Argon2i
 54// returning a byte slice of length keyLen that can be used as cryptographic
 55// key. The CPU cost and parallelism degree must be greater than zero.
 56//
 57// For example, you can get a derived key for e.g. AES-256 (which needs a
 58// 32-byte key) by doing:
 59//
 60//	key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
 61//
 62// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
 63// If using that amount of memory (32 MB) is not possible in some contexts then
 64// the time parameter can be increased to compensate.
 65//
 66// The time parameter specifies the number of passes over the memory and the
 67// memory parameter specifies the size of the memory in KiB. For example
 68// memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
 69// adjusted to the number of available CPUs. The cost parameters should be
 70// increased as memory latency and CPU parallelism increases. Remember to get a
 71// good random salt.
 72func 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// IDKey derives a key from the password, salt, and cost parameters using
 77// Argon2id returning a byte slice of length keyLen that can be used as
 78// cryptographic key. The CPU cost and parallelism degree must be greater than
 79// zero.
 80//
 81// For example, you can get a derived key for e.g. AES-256 (which needs a
 82// 32-byte key) by doing:
 83//
 84//	key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
 85//
 86// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
 87// If using that amount of memory (64 MB) is not possible in some contexts then
 88// the time parameter can be increased to compensate.
 89//
 90// The time parameter specifies the number of passes over the memory and the
 91// memory parameter specifies the size of the memory in KiB. For example
 92// memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
 93// adjusted to the numbers of available CPUs. The cost parameters should be
 94// increased as memory latency and CPU parallelism increases. Remember to get a
 95// good random salt.
 96func 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
100func 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
118const (
119	blockLength = 128
120	syncPoints  = 4
121)
122
123type block [blockLength]uint64
124
125func 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
156func 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
178func 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 // we have already generated the first two blocks
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 // last block in lane
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
240func 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
257func 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
278func 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}