...

Source file src/golang.org/x/crypto/argon2/argon2.go

Documentation: golang.org/x/crypto/argon2

     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  }
   284  

View as plain text