123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- // Copyright 2017 The Go Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style
- // license that can be found in the LICENSE file.
- // Package argon2 implements the key derivation function Argon2.
- // Argon2 was selected as the winner of the Password Hashing Competition and can
- // be used to derive cryptographic keys from passwords.
- //
- // For a detailed specification of Argon2 see [1].
- //
- // If you aren't sure which function you need, use Argon2id (IDKey) and
- // the parameter recommendations for your scenario.
- //
- //
- // Argon2i
- //
- // Argon2i (implemented by Key) is the side-channel resistant version of Argon2.
- // It uses data-independent memory access, which is preferred for password
- // hashing and password-based key derivation. Argon2i requires more passes over
- // memory than Argon2id to protect from trade-off attacks. The recommended
- // parameters (taken from [2]) for non-interactive operations are time=3 and to
- // use the maximum available memory.
- //
- //
- // Argon2id
- //
- // Argon2id (implemented by IDKey) is a hybrid version of Argon2 combining
- // Argon2i and Argon2d. It uses data-independent memory access for the first
- // half of the first iteration over the memory and data-dependent memory access
- // for the rest. Argon2id is side-channel resistant and provides better brute-
- // force cost savings due to time-memory tradeoffs than Argon2i. The recommended
- // parameters for non-interactive operations (taken from [2]) are time=1 and to
- // use the maximum available memory.
- //
- // [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
- // [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
- package argon2
- import (
- "encoding/binary"
- "sync"
- "golang.org/x/crypto/blake2b"
- )
- // The Argon2 version implemented by this package.
- const Version = 0x13
- const (
- argon2d = iota
- argon2i
- argon2id
- )
- // Key derives a key from the password, salt, and cost parameters using Argon2i
- // returning a byte slice of length keyLen that can be used as cryptographic
- // key. The CPU cost and parallelism degree must be greater than zero.
- //
- // For example, you can get a derived key for e.g. AES-256 (which needs a
- // 32-byte key) by doing:
- //
- // key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
- //
- // The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
- // If using that amount of memory (32 MB) is not possible in some contexts then
- // the time parameter can be increased to compensate.
- //
- // The time parameter specifies the number of passes over the memory and the
- // memory parameter specifies the size of the memory in KiB. For example
- // memory=32*1024 sets the memory cost to ~32 MB. The number of threads can be
- // adjusted to the number of available CPUs. The cost parameters should be
- // increased as memory latency and CPU parallelism increases. Remember to get a
- // good random salt.
- func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
- return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
- }
- // IDKey derives a key from the password, salt, and cost parameters using
- // Argon2id returning a byte slice of length keyLen that can be used as
- // cryptographic key. The CPU cost and parallelism degree must be greater than
- // zero.
- //
- // For example, you can get a derived key for e.g. AES-256 (which needs a
- // 32-byte key) by doing:
- //
- // key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
- //
- // The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
- // If using that amount of memory (64 MB) is not possible in some contexts then
- // the time parameter can be increased to compensate.
- //
- // The time parameter specifies the number of passes over the memory and the
- // memory parameter specifies the size of the memory in KiB. For example
- // memory=64*1024 sets the memory cost to ~64 MB. The number of threads can be
- // adjusted to the numbers of available CPUs. The cost parameters should be
- // increased as memory latency and CPU parallelism increases. Remember to get a
- // good random salt.
- func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
- return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
- }
- func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
- if time < 1 {
- panic("argon2: number of rounds too small")
- }
- if threads < 1 {
- panic("argon2: parallelism degree too low")
- }
- h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode)
- memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads))
- if memory < 2*syncPoints*uint32(threads) {
- memory = 2 * syncPoints * uint32(threads)
- }
- B := initBlocks(&h0, memory, uint32(threads))
- processBlocks(B, time, memory, uint32(threads), mode)
- return extractKey(B, memory, uint32(threads), keyLen)
- }
- const (
- blockLength = 128
- syncPoints = 4
- )
- type block [blockLength]uint64
- func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte {
- var (
- h0 [blake2b.Size + 8]byte
- params [24]byte
- tmp [4]byte
- )
- b2, _ := blake2b.New512(nil)
- binary.LittleEndian.PutUint32(params[0:4], threads)
- binary.LittleEndian.PutUint32(params[4:8], keyLen)
- binary.LittleEndian.PutUint32(params[8:12], memory)
- binary.LittleEndian.PutUint32(params[12:16], time)
- binary.LittleEndian.PutUint32(params[16:20], uint32(Version))
- binary.LittleEndian.PutUint32(params[20:24], uint32(mode))
- b2.Write(params[:])
- binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))
- b2.Write(tmp[:])
- b2.Write(password)
- binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))
- b2.Write(tmp[:])
- b2.Write(salt)
- binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))
- b2.Write(tmp[:])
- b2.Write(key)
- binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))
- b2.Write(tmp[:])
- b2.Write(data)
- b2.Sum(h0[:0])
- return h0
- }
- func initBlocks(h0 *[blake2b.Size + 8]byte, memory, threads uint32) []block {
- var block0 [1024]byte
- B := make([]block, memory)
- for lane := uint32(0); lane < threads; lane++ {
- j := lane * (memory / threads)
- binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)
- binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)
- blake2bHash(block0[:], h0[:])
- for i := range B[j+0] {
- B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])
- }
- binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)
- blake2bHash(block0[:], h0[:])
- for i := range B[j+1] {
- B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
- }
- }
- return B
- }
- func processBlocks(B []block, time, memory, threads uint32, mode int) {
- lanes := memory / threads
- segments := lanes / syncPoints
- processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {
- var addresses, in, zero block
- if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
- in[0] = uint64(n)
- in[1] = uint64(lane)
- in[2] = uint64(slice)
- in[3] = uint64(memory)
- in[4] = uint64(time)
- in[5] = uint64(mode)
- }
- index := uint32(0)
- if n == 0 && slice == 0 {
- index = 2 // we have already generated the first two blocks
- if mode == argon2i || mode == argon2id {
- in[6]++
- processBlock(&addresses, &in, &zero)
- processBlock(&addresses, &addresses, &zero)
- }
- }
- offset := lane*lanes + slice*segments + index
- var random uint64
- for index < segments {
- prev := offset - 1
- if index == 0 && slice == 0 {
- prev += lanes // last block in lane
- }
- if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {
- if index%blockLength == 0 {
- in[6]++
- processBlock(&addresses, &in, &zero)
- processBlock(&addresses, &addresses, &zero)
- }
- random = addresses[index%blockLength]
- } else {
- random = B[prev][0]
- }
- newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)
- processBlockXOR(&B[offset], &B[prev], &B[newOffset])
- index, offset = index+1, offset+1
- }
- wg.Done()
- }
- for n := uint32(0); n < time; n++ {
- for slice := uint32(0); slice < syncPoints; slice++ {
- var wg sync.WaitGroup
- for lane := uint32(0); lane < threads; lane++ {
- wg.Add(1)
- go processSegment(n, slice, lane, &wg)
- }
- wg.Wait()
- }
- }
- }
- func extractKey(B []block, memory, threads, keyLen uint32) []byte {
- lanes := memory / threads
- for lane := uint32(0); lane < threads-1; lane++ {
- for i, v := range B[(lane*lanes)+lanes-1] {
- B[memory-1][i] ^= v
- }
- }
- var block [1024]byte
- for i, v := range B[memory-1] {
- binary.LittleEndian.PutUint64(block[i*8:], v)
- }
- key := make([]byte, keyLen)
- blake2bHash(key, block[:])
- return key
- }
- func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {
- refLane := uint32(rand>>32) % threads
- if n == 0 && slice == 0 {
- refLane = lane
- }
- m, s := 3*segments, ((slice+1)%syncPoints)*segments
- if lane == refLane {
- m += index
- }
- if n == 0 {
- m, s = slice*segments, 0
- if slice == 0 || lane == refLane {
- m += index
- }
- }
- if index == 0 || lane == refLane {
- m--
- }
- return phi(rand, uint64(m), uint64(s), refLane, lanes)
- }
- func phi(rand, m, s uint64, lane, lanes uint32) uint32 {
- p := rand & 0xFFFFFFFF
- p = (p * p) >> 32
- p = (p * m) >> 32
- return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))
- }
|