storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/pkg/argon2/argon2.go (about) 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 https://golang.org/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 // 15 // Argon2i 16 // 17 // Argon2i (implemented by Key) is the side-channel resistant version of Argon2. 18 // It uses data-independent memory access, which is preferred for password 19 // hashing and password-based key derivation. Argon2i requires more passes over 20 // memory than Argon2id to protect from trade-off attacks. The recommended 21 // parameters (taken from [2]) for non-interactive operations are time=3 and to 22 // use the maximum available memory. 23 // 24 // 25 // Argon2id 26 // 27 // Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining 28 // Argon2i and Argon2d. It uses data-independent memory access for the first 29 // half of the first iteration over the memory and data-dependent memory access 30 // for the rest. Argon2id is side-channel resistant and provides better brute- 31 // force cost savings due to time-memory tradeoffs than Argon2i. The recommended 32 // parameters for non-interactive operations (taken from [2]) are time=1 and to 33 // use the maximum available memory. 34 // 35 // [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf 36 // [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3 37 // 38 // This package is a fork of golang.org/x/crypto/argon2 adding support for 39 // sync.Pool reusable buffers to avoid large memory build up with frequent 40 // allocations done by memory hard PBKDF. 41 // 42 // All the changes are governed by the LICENSE file MinIO project. 43 package argon2 44 45 import ( 46 "encoding/binary" 47 "sync" 48 49 "golang.org/x/crypto/blake2b" 50 ) 51 52 // The Argon2 version implemented by this package. 53 const Version = 0x13 54 55 const ( 56 argon2d = iota 57 argon2i 58 argon2id 59 ) 60 61 // Key derives a key from the password, salt, and cost parameters using Argon2i 62 // returning a byte slice of length keyLen that can be used as cryptographic 63 // key. The CPU cost and parallelism degree must be greater than zero. 64 // 65 // For example, you can get a derived key for e.g. AES-256 (which needs a 66 // 32-byte key) by doing: 67 // 68 // key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32) 69 // 70 // The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number. 71 // If using that amount of memory (32 MB) is not possible in some contexts then 72 // the time parameter can be increased to compensate. 73 // 74 // The time parameter specifies the number of passes over the memory and the 75 // memory parameter specifies the size of the memory in KiB. For example 76 // memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be 77 // adjusted to the number of available CPUs. The cost parameters should be 78 // increased as memory latency and CPU parallelism increases. Remember to get a 79 // good random salt. 80 func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte { 81 return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen) 82 } 83 84 // IDKey derives a key from the password, salt, and cost parameters using 85 // Argon2id returning a byte slice of length keyLen that can be used as 86 // cryptographic key. The CPU cost and parallelism degree must be greater than 87 // zero. 88 // 89 // For example, you can get a derived key for e.g. AES-256 (which needs a 90 // 32-byte key) by doing: 91 // 92 // key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32) 93 // 94 // The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number. 95 // If using that amount of memory (64 MB) is not possible in some contexts then 96 // the time parameter can be increased to compensate. 97 // 98 // The time parameter specifies the number of passes over the memory and the 99 // memory parameter specifies the size of the memory in KiB. For example 100 // memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be 101 // adjusted to the numbers of available CPUs. The cost parameters should be 102 // increased as memory latency and CPU parallelism increases. Remember to get a 103 // good random salt. 104 func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte { 105 return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen) 106 } 107 108 func clearBlocks(B []block) { 109 for i := range B { 110 B[i] = block{} 111 } 112 } 113 114 // NewIDKey returns an argon2 PBKDF backend by sync.Pool 115 func NewIDKey(time, memory uint32, threads uint8) func([]byte, []byte, []byte, []byte, uint32) []byte { 116 if time < 1 { 117 panic("argon2: number of rounds too small") 118 } 119 if threads < 1 { 120 panic("argon2: parallelism degree too low") 121 } 122 123 hashMemory := memory 124 125 memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads)) 126 if memory < 2*syncPoints*uint32(threads) { 127 memory = 2 * syncPoints * uint32(threads) 128 } 129 130 pool := sync.Pool{ 131 New: func() interface{} { 132 b := make([]block, memory) 133 return &b 134 }, 135 } 136 137 return func(password, salt, secret, data []byte, keyLen uint32) []byte { 138 B := pool.Get().(*[]block) 139 defer func() { 140 clearBlocks(*B) 141 pool.Put(B) 142 }() 143 144 h0 := initHash(password, salt, secret, data, time, hashMemory, uint32(threads), keyLen, argon2id) 145 B1 := initBlocks(&h0, *B, uint32(threads)) 146 processBlocks(B1, time, memory, uint32(threads), argon2id) 147 return extractKey(B1, memory, uint32(threads), keyLen) 148 } 149 } 150 151 func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte { 152 if time < 1 { 153 panic("argon2: number of rounds too small") 154 } 155 if threads < 1 { 156 panic("argon2: parallelism degree too low") 157 } 158 h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode) 159 160 memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads)) 161 if memory < 2*syncPoints*uint32(threads) { 162 memory = 2 * syncPoints * uint32(threads) 163 } 164 B := make([]block, memory) 165 B = initBlocks(&h0, B, uint32(threads)) 166 processBlocks(B, time, memory, uint32(threads), mode) 167 return extractKey(B, memory, uint32(threads), keyLen) 168 } 169 170 const ( 171 blockLength = 128 172 syncPoints = 4 173 ) 174 175 type block [blockLength]uint64 176 177 func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte { 178 var ( 179 h0 [blake2b.Size + 8]byte 180 params [24]byte 181 tmp [4]byte 182 ) 183 184 b2, _ := blake2b.New512(nil) 185 binary.LittleEndian.PutUint32(params[0:4], threads) 186 binary.LittleEndian.PutUint32(params[4:8], keyLen) 187 binary.LittleEndian.PutUint32(params[8:12], memory) 188 binary.LittleEndian.PutUint32(params[12:16], time) 189 binary.LittleEndian.PutUint32(params[16:20], uint32(Version)) 190 binary.LittleEndian.PutUint32(params[20:24], uint32(mode)) 191 b2.Write(params[:]) 192 binary.LittleEndian.PutUint32(tmp[:], uint32(len(password))) 193 b2.Write(tmp[:]) 194 b2.Write(password) 195 binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt))) 196 b2.Write(tmp[:]) 197 b2.Write(salt) 198 binary.LittleEndian.PutUint32(tmp[:], uint32(len(key))) 199 b2.Write(tmp[:]) 200 b2.Write(key) 201 binary.LittleEndian.PutUint32(tmp[:], uint32(len(data))) 202 b2.Write(tmp[:]) 203 b2.Write(data) 204 b2.Sum(h0[:0]) 205 return h0 206 } 207 208 func initBlocks(h0 *[blake2b.Size + 8]byte, blocks []block, threads uint32) []block { 209 var block0 [1024]byte 210 B := blocks 211 for lane := uint32(0); lane < threads; lane++ { 212 j := lane * (uint32(len(B)) / threads) 213 binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane) 214 215 binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0) 216 blake2bHash(block0[:], h0[:]) 217 for i := range B[j+0] { 218 B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:]) 219 } 220 221 binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1) 222 blake2bHash(block0[:], h0[:]) 223 for i := range B[j+1] { 224 B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:]) 225 } 226 } 227 return B 228 } 229 230 func processBlocks(B []block, time, memory, threads uint32, mode int) { 231 lanes := memory / threads 232 segments := lanes / syncPoints 233 234 processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) { 235 var addresses, in, zero block 236 if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) { 237 in[0] = uint64(n) 238 in[1] = uint64(lane) 239 in[2] = uint64(slice) 240 in[3] = uint64(memory) 241 in[4] = uint64(time) 242 in[5] = uint64(mode) 243 } 244 245 index := uint32(0) 246 if n == 0 && slice == 0 { 247 index = 2 // we have already generated the first two blocks 248 if mode == argon2i || mode == argon2id { 249 in[6]++ 250 processBlock(&addresses, &in, &zero) 251 processBlock(&addresses, &addresses, &zero) 252 } 253 } 254 255 offset := lane*lanes + slice*segments + index 256 var random uint64 257 for index < segments { 258 prev := offset - 1 259 if index == 0 && slice == 0 { 260 prev += lanes // last block in lane 261 } 262 if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) { 263 if index%blockLength == 0 { 264 in[6]++ 265 processBlock(&addresses, &in, &zero) 266 processBlock(&addresses, &addresses, &zero) 267 } 268 random = addresses[index%blockLength] 269 } else { 270 random = B[prev][0] 271 } 272 newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index) 273 processBlockXOR(&B[offset], &B[prev], &B[newOffset]) 274 index, offset = index+1, offset+1 275 } 276 wg.Done() 277 } 278 279 for n := uint32(0); n < time; n++ { 280 for slice := uint32(0); slice < syncPoints; slice++ { 281 var wg sync.WaitGroup 282 for lane := uint32(0); lane < threads; lane++ { 283 wg.Add(1) 284 go processSegment(n, slice, lane, &wg) 285 } 286 wg.Wait() 287 } 288 } 289 290 } 291 292 func extractKey(B []block, memory, threads, keyLen uint32) []byte { 293 lanes := memory / threads 294 for lane := uint32(0); lane < threads-1; lane++ { 295 for i, v := range B[(lane*lanes)+lanes-1] { 296 B[memory-1][i] ^= v 297 } 298 } 299 300 var block [1024]byte 301 for i, v := range B[memory-1] { 302 binary.LittleEndian.PutUint64(block[i*8:], v) 303 } 304 key := make([]byte, keyLen) 305 blake2bHash(key, block[:]) 306 return key 307 } 308 309 func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 { 310 refLane := uint32(rand>>32) % threads 311 if n == 0 && slice == 0 { 312 refLane = lane 313 } 314 m, s := 3*segments, ((slice+1)%syncPoints)*segments 315 if lane == refLane { 316 m += index 317 } 318 if n == 0 { 319 m, s = slice*segments, 0 320 if slice == 0 || lane == refLane { 321 m += index 322 } 323 } 324 if index == 0 || lane == refLane { 325 m-- 326 } 327 return phi(rand, uint64(m), uint64(s), refLane, lanes) 328 } 329 330 func phi(rand, m, s uint64, lane, lanes uint32) uint32 { 331 p := rand & 0xFFFFFFFF 332 p = (p * p) >> 32 333 p = (p * m) >> 32 334 return lane*lanes + uint32((s+m-(p+1))%uint64(lanes)) 335 }