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  }