gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

argon2.go (9415B)


      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
     35 package argon2
     36 
     37 import (
     38 	"encoding/binary"
     39 	"sync"
     40 
     41 	"golang.org/x/crypto/blake2b"
     42 )
     43 
     44 // The Argon2 version implemented by this package.
     45 const Version = 0x13
     46 
     47 const (
     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.
     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 // 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.
     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 // 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 
    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 }