本站源代码
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

286 lines
9.2KB

  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. // Package argon2 implements the key derivation function Argon2.
  5. // Argon2 was selected as the winner of the Password Hashing Competition and can
  6. // be used to derive cryptographic keys from passwords.
  7. //
  8. // For a detailed specification of Argon2 see [1].
  9. //
  10. // If you aren't sure which function you need, use Argon2id (IDKey) and
  11. // the parameter recommendations for your scenario.
  12. //
  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. //
  24. // Argon2id
  25. //
  26. // Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
  27. // Argon2i and Argon2d. It uses data-independent memory access for the first
  28. // half of the first iteration over the memory and data-dependent memory access
  29. // for the rest. Argon2id is side-channel resistant and provides better brute-
  30. // force cost savings due to time-memory tradeoffs than Argon2i. The recommended
  31. // parameters for non-interactive operations (taken from [2]) are time=1 and to
  32. // use the maximum available memory.
  33. //
  34. // [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
  35. // [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
  36. package argon2
  37. import (
  38. "encoding/binary"
  39. "sync"
  40. "golang.org/x/crypto/blake2b"
  41. )
  42. // The Argon2 version implemented by this package.
  43. const Version = 0x13
  44. const (
  45. argon2d = iota
  46. argon2i
  47. argon2id
  48. )
  49. // Key derives a key from the password, salt, and cost parameters using Argon2i
  50. // returning a byte slice of length keyLen that can be used as cryptographic
  51. // key. The CPU cost and parallelism degree must be greater than zero.
  52. //
  53. // For example, you can get a derived key for e.g. AES-256 (which needs a
  54. // 32-byte key) by doing:
  55. //
  56. // key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
  57. //
  58. // The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
  59. // If using that amount of memory (32 MB) is not possible in some contexts then
  60. // the time parameter can be increased to compensate.
  61. //
  62. // The time parameter specifies the number of passes over the memory and the
  63. // memory parameter specifies the size of the memory in KiB. For example
  64. // memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
  65. // adjusted to the number of available CPUs. The cost parameters should be
  66. // increased as memory latency and CPU parallelism increases. Remember to get a
  67. // good random salt.
  68. func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
  69. return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
  70. }
  71. // IDKey derives a key from the password, salt, and cost parameters using
  72. // Argon2id returning a byte slice of length keyLen that can be used as
  73. // cryptographic key. The CPU cost and parallelism degree must be greater than
  74. // zero.
  75. //
  76. // For example, you can get a derived key for e.g. AES-256 (which needs a
  77. // 32-byte key) by doing:
  78. //
  79. // key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
  80. //
  81. // The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
  82. // If using that amount of memory (64 MB) is not possible in some contexts then
  83. // the time parameter can be increased to compensate.
  84. //
  85. // The time parameter specifies the number of passes over the memory and the
  86. // memory parameter specifies the size of the memory in KiB. For example
  87. // memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
  88. // adjusted to the numbers of available CPUs. The cost parameters should be
  89. // increased as memory latency and CPU parallelism increases. Remember to get a
  90. // good random salt.
  91. func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
  92. return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
  93. }
  94. func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
  95. if time < 1 {
  96. panic("argon2: number of rounds too small")
  97. }
  98. if threads < 1 {
  99. panic("argon2: parallelism degree too low")
  100. }
  101. h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode)
  102. memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads))
  103. if memory < 2*syncPoints*uint32(threads) {
  104. memory = 2 * syncPoints * uint32(threads)
  105. }
  106. B := initBlocks(&h0, memory, uint32(threads))
  107. processBlocks(B, time, memory, uint32(threads), mode)
  108. return extractKey(B, memory, uint32(threads), keyLen)
  109. }
  110. const (
  111. blockLength = 128
  112. syncPoints = 4
  113. )
  114. type block [blockLength]uint64
  115. func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte {
  116. var (
  117. h0 [blake2b.Size + 8]byte
  118. params [24]byte
  119. tmp [4]byte
  120. )
  121. b2, _ := blake2b.New512(nil)
  122. binary.LittleEndian.PutUint32(params[0:4], threads)
  123. binary.LittleEndian.PutUint32(params[4:8], keyLen)
  124. binary.LittleEndian.PutUint32(params[8:12], memory)
  125. binary.LittleEndian.PutUint32(params[12:16], time)
  126. binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
  127. binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
  128. b2.Write(params[:])
  129. binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
  130. b2.Write(tmp[:])
  131. b2.Write(password)
  132. binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
  133. b2.Write(tmp[:])
  134. b2.Write(salt)
  135. binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
  136. b2.Write(tmp[:])
  137. b2.Write(key)
  138. binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
  139. b2.Write(tmp[:])
  140. b2.Write(data)
  141. b2.Sum(h0[:0])
  142. return h0
  143. }
  144. func initBlocks(h0 *[blake2b.Size + 8]byte, memory, threads uint32) []block {
  145. var block0 [1024]byte
  146. B := make([]block, memory)
  147. for lane := uint32(0); lane < threads; lane++ {
  148. j := lane * (memory / threads)
  149. binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
  150. binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
  151. blake2bHash(block0[:], h0[:])
  152. for i := range B[j+0] {
  153. B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
  154. }
  155. binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
  156. blake2bHash(block0[:], h0[:])
  157. for i := range B[j+1] {
  158. B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
  159. }
  160. }
  161. return B
  162. }
  163. func processBlocks(B []block, time, memory, threads uint32, mode int) {
  164. lanes := memory / threads
  165. segments := lanes / syncPoints
  166. processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
  167. var addresses, in, zero block
  168. if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
  169. in[0] = uint64(n)
  170. in[1] = uint64(lane)
  171. in[2] = uint64(slice)
  172. in[3] = uint64(memory)
  173. in[4] = uint64(time)
  174. in[5] = uint64(mode)
  175. }
  176. index := uint32(0)
  177. if n == 0 && slice == 0 {
  178. index = 2 // we have already generated the first two blocks
  179. if mode == argon2i || mode == argon2id {
  180. in[6]++
  181. processBlock(&addresses, &in, &zero)
  182. processBlock(&addresses, &addresses, &zero)
  183. }
  184. }
  185. offset := lane*lanes + slice*segments + index
  186. var random uint64
  187. for index < segments {
  188. prev := offset - 1
  189. if index == 0 && slice == 0 {
  190. prev += lanes // last block in lane
  191. }
  192. if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
  193. if index%blockLength == 0 {
  194. in[6]++
  195. processBlock(&addresses, &in, &zero)
  196. processBlock(&addresses, &addresses, &zero)
  197. }
  198. random = addresses[index%blockLength]
  199. } else {
  200. random = B[prev][0]
  201. }
  202. newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
  203. processBlockXOR(&B[offset], &B[prev], &B[newOffset])
  204. index, offset = index+1, offset+1
  205. }
  206. wg.Done()
  207. }
  208. for n := uint32(0); n < time; n++ {
  209. for slice := uint32(0); slice < syncPoints; slice++ {
  210. var wg sync.WaitGroup
  211. for lane := uint32(0); lane < threads; lane++ {
  212. wg.Add(1)
  213. go processSegment(n, slice, lane, &wg)
  214. }
  215. wg.Wait()
  216. }
  217. }
  218. }
  219. func extractKey(B []block, memory, threads, keyLen uint32) []byte {
  220. lanes := memory / threads
  221. for lane := uint32(0); lane < threads-1; lane++ {
  222. for i, v := range B[(lane*lanes)+lanes-1] {
  223. B[memory-1][i] ^= v
  224. }
  225. }
  226. var block [1024]byte
  227. for i, v := range B[memory-1] {
  228. binary.LittleEndian.PutUint64(block[i*8:], v)
  229. }
  230. key := make([]byte, keyLen)
  231. blake2bHash(key, block[:])
  232. return key
  233. }
  234. func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
  235. refLane := uint32(rand>>32) % threads
  236. if n == 0 && slice == 0 {
  237. refLane = lane
  238. }
  239. m, s := 3*segments, ((slice+1)%syncPoints)*segments
  240. if lane == refLane {
  241. m += index
  242. }
  243. if n == 0 {
  244. m, s = slice*segments, 0
  245. if slice == 0 || lane == refLane {
  246. m += index
  247. }
  248. }
  249. if index == 0 || lane == refLane {
  250. m--
  251. }
  252. return phi(rand, uint64(m), uint64(s), refLane, lanes)
  253. }
  254. func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
  255. p := rand & 0xFFFFFFFF
  256. p = (p * p) >> 32
  257. p = (p * m) >> 32
  258. return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
  259. }
上海开阖软件有限公司 沪ICP备12045867号-1