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.

219 lines
6.5 KiB

  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. // Argon2 is specfifed at https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
  8. package argon2
  9. import (
  10. "encoding/binary"
  11. "sync"
  12. "golang.org/x/crypto/blake2b"
  13. )
  14. // The Argon2 version implemented by this package.
  15. const Version = 0x13
  16. const (
  17. argon2d = iota
  18. argon2i
  19. argon2id
  20. )
  21. // Key derives a key from the password, salt, and cost parameters using Argon2i
  22. // returning a byte slice of length keyLen that can be used as cryptographic key.
  23. // The CPU cost and parallism degree must be greater than zero.
  24. //
  25. // For example, you can get a derived key for e.g. AES-256 (which needs a 32-byte key) by doing:
  26. // `key := argon2.Key([]byte("some password"), salt, 4, 32*1024, 4, 32)`
  27. //
  28. // The recommended parameters for interactive logins as of 2017 are time=4, memory=32*1024.
  29. // The number of threads can be adjusted to the numbers of available CPUs.
  30. // The time parameter specifies the number of passes over the memory and the memory
  31. // parameter specifies the size of the memory in KiB. For example memory=32*1024 sets the
  32. // memory cost to ~32 MB.
  33. // The cost parameters should be increased as memory latency and CPU parallelism increases.
  34. // Remember to get a good random salt.
  35. func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
  36. return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
  37. }
  38. func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
  39. if time < 1 {
  40. panic("argon2: number of rounds too small")
  41. }
  42. if threads < 1 {
  43. panic("argon2: paralisim degree too low")
  44. }
  45. mem := memory / (4 * uint32(threads)) * (4 * uint32(threads))
  46. if mem < 8*uint32(threads) {
  47. mem = 8 * uint32(threads)
  48. }
  49. B := initBlocks(password, salt, secret, data, time, mem, uint32(threads), keyLen, mode)
  50. processBlocks(B, time, mem, uint32(threads), mode)
  51. return extractKey(B, mem, uint32(threads), keyLen)
  52. }
  53. const blockLength = 128
  54. type block [blockLength]uint64
  55. func initBlocks(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) []block {
  56. var (
  57. block0 [1024]byte
  58. h0 [blake2b.Size + 8]byte
  59. params [24]byte
  60. tmp [4]byte
  61. )
  62. b2, _ := blake2b.New512(nil)
  63. binary.LittleEndian.PutUint32(params[0:4], threads)
  64. binary.LittleEndian.PutUint32(params[4:8], keyLen)
  65. binary.LittleEndian.PutUint32(params[8:12], memory)
  66. binary.LittleEndian.PutUint32(params[12:16], time)
  67. binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
  68. binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
  69. b2.Write(params[:])
  70. binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
  71. b2.Write(tmp[:])
  72. b2.Write(password)
  73. binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
  74. b2.Write(tmp[:])
  75. b2.Write(salt)
  76. binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
  77. b2.Write(tmp[:])
  78. b2.Write(key)
  79. binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
  80. b2.Write(tmp[:])
  81. b2.Write(data)
  82. b2.Sum(h0[:0])
  83. B := make([]block, memory)
  84. for lane := uint32(0); lane < threads; lane++ {
  85. j := lane * (memory / threads)
  86. binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
  87. binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
  88. blake2bHash(block0[:], h0[:])
  89. for i := range B[0] {
  90. B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
  91. }
  92. binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
  93. blake2bHash(block0[:], h0[:])
  94. for i := range B[0] {
  95. B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
  96. }
  97. }
  98. return B
  99. }
  100. func processBlocks(B []block, time, memory, threads uint32, mode int) {
  101. const syncPoints = 4
  102. lanes := memory / threads
  103. segments := lanes / syncPoints
  104. processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
  105. var addresses, in, zero block
  106. if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
  107. in[0] = uint64(n)
  108. in[1] = uint64(lane)
  109. in[2] = uint64(slice)
  110. in[3] = uint64(memory)
  111. in[4] = uint64(time)
  112. in[5] = uint64(mode)
  113. }
  114. index := uint32(0)
  115. if n == 0 && slice == 0 {
  116. index = 2 // we have already generated the first two blocks
  117. if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
  118. in[6]++
  119. processBlock(&addresses, &in, &zero)
  120. processBlock(&addresses, &addresses, &zero)
  121. }
  122. }
  123. offset := lane*lanes + slice*segments + index
  124. var random uint64
  125. for index < segments {
  126. prev := offset - 1
  127. if index == 0 && slice == 0 {
  128. prev = lane*lanes + lanes - 1 // last block in lane
  129. }
  130. if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
  131. if index%blockLength == 0 {
  132. in[6]++
  133. processBlock(&addresses, &in, &zero)
  134. processBlock(&addresses, &addresses, &zero)
  135. }
  136. random = addresses[index%blockLength]
  137. } else {
  138. random = B[prev][0]
  139. }
  140. newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
  141. processBlockXOR(&B[offset], &B[prev], &B[newOffset])
  142. index, offset = index+1, offset+1
  143. }
  144. wg.Done()
  145. }
  146. for n := uint32(0); n < time; n++ {
  147. for slice := uint32(0); slice < syncPoints; slice++ {
  148. var wg sync.WaitGroup
  149. for lane := uint32(0); lane < threads; lane++ {
  150. wg.Add(1)
  151. go processSegment(n, slice, lane, &wg)
  152. }
  153. wg.Wait()
  154. }
  155. }
  156. }
  157. func extractKey(B []block, memory, threads, keyLen uint32) []byte {
  158. lanes := memory / threads
  159. for lane := uint32(0); lane < threads-1; lane++ {
  160. for i, v := range B[(lane*lanes)+lanes-1] {
  161. B[memory-1][i] ^= v
  162. }
  163. }
  164. var block [1024]byte
  165. for i, v := range B[memory-1] {
  166. binary.LittleEndian.PutUint64(block[i*8:], v)
  167. }
  168. key := make([]byte, keyLen)
  169. blake2bHash(key, block[:])
  170. return key
  171. }
  172. func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
  173. refLane := uint32(rand>>32) % threads
  174. m, s := 3*segments, (slice+1)%4*segments
  175. if lane == refLane {
  176. m += index
  177. }
  178. if n == 0 {
  179. m, s = slice*segments, 0
  180. if slice == 0 || lane == refLane {
  181. m += index
  182. }
  183. }
  184. if index == 0 || lane == refLane {
  185. m--
  186. }
  187. return phi(rand, uint64(m), uint64(s), refLane, lanes)
  188. }
  189. func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
  190. p := rand & 0xFFFFFFFF
  191. p = (p * p) >> 32
  192. p = (p * m) >> 32
  193. return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
  194. }