podman
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
35package argon236
37import (38"encoding/binary"39"sync"40
41"golang.org/x/crypto/blake2b"42)
43
44// The Argon2 version implemented by this package.
45const Version = 0x1346
47const (48argon2d = iota49argon2i
50argon2id
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.
72func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {73return 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.
96func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {97return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)98}
99
100func deriveKey(mode int, password, salt, secret, data []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {101if time < 1 {102panic("argon2: number of rounds too small")103}104if threads < 1 {105panic("argon2: parallelism degree too low")106}107h0 := initHash(password, salt, secret, data, time, memory, uint32(threads), keyLen, mode)108
109memory = memory / (syncPoints * uint32(threads)) * (syncPoints * uint32(threads))110if memory < 2*syncPoints*uint32(threads) {111memory = 2 * syncPoints * uint32(threads)112}113B := initBlocks(&h0, memory, uint32(threads))114processBlocks(B, time, memory, uint32(threads), mode)115return extractKey(B, memory, uint32(threads), keyLen)116}
117
118const (119blockLength = 128120syncPoints = 4121)
122
123type block [blockLength]uint64124
125func initHash(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) [blake2b.Size + 8]byte {126var (127h0 [blake2b.Size + 8]byte128params [24]byte129tmp [4]byte130)131
132b2, _ := blake2b.New512(nil)133binary.LittleEndian.PutUint32(params[0:4], threads)134binary.LittleEndian.PutUint32(params[4:8], keyLen)135binary.LittleEndian.PutUint32(params[8:12], memory)136binary.LittleEndian.PutUint32(params[12:16], time)137binary.LittleEndian.PutUint32(params[16:20], uint32(Version))138binary.LittleEndian.PutUint32(params[20:24], uint32(mode))139b2.Write(params[:])140binary.LittleEndian.PutUint32(tmp[:], uint32(len(password)))141b2.Write(tmp[:])142b2.Write(password)143binary.LittleEndian.PutUint32(tmp[:], uint32(len(salt)))144b2.Write(tmp[:])145b2.Write(salt)146binary.LittleEndian.PutUint32(tmp[:], uint32(len(key)))147b2.Write(tmp[:])148b2.Write(key)149binary.LittleEndian.PutUint32(tmp[:], uint32(len(data)))150b2.Write(tmp[:])151b2.Write(data)152b2.Sum(h0[:0])153return h0154}
155
156func initBlocks(h0 *[blake2b.Size + 8]byte, memory, threads uint32) []block {157var block0 [1024]byte158B := make([]block, memory)159for lane := uint32(0); lane < threads; lane++ {160j := lane * (memory / threads)161binary.LittleEndian.PutUint32(h0[blake2b.Size+4:], lane)162
163binary.LittleEndian.PutUint32(h0[blake2b.Size:], 0)164blake2bHash(block0[:], h0[:])165for i := range B[j+0] {166B[j+0][i] = binary.LittleEndian.Uint64(block0[i*8:])167}168
169binary.LittleEndian.PutUint32(h0[blake2b.Size:], 1)170blake2bHash(block0[:], h0[:])171for i := range B[j+1] {172B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])173}174}175return B176}
177
178func processBlocks(B []block, time, memory, threads uint32, mode int) {179lanes := memory / threads180segments := lanes / syncPoints181
182processSegment := func(n, slice, lane uint32, wg *sync.WaitGroup) {183var addresses, in, zero block184if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {185in[0] = uint64(n)186in[1] = uint64(lane)187in[2] = uint64(slice)188in[3] = uint64(memory)189in[4] = uint64(time)190in[5] = uint64(mode)191}192
193index := uint32(0)194if n == 0 && slice == 0 {195index = 2 // we have already generated the first two blocks196if mode == argon2i || mode == argon2id {197in[6]++198processBlock(&addresses, &in, &zero)199processBlock(&addresses, &addresses, &zero)200}201}202
203offset := lane*lanes + slice*segments + index204var random uint64205for index < segments {206prev := offset - 1207if index == 0 && slice == 0 {208prev += lanes // last block in lane209}210if mode == argon2i || (mode == argon2id && n == 0 && slice < syncPoints/2) {211if index%blockLength == 0 {212in[6]++213processBlock(&addresses, &in, &zero)214processBlock(&addresses, &addresses, &zero)215}216random = addresses[index%blockLength]217} else {218random = B[prev][0]219}220newOffset := indexAlpha(random, lanes, segments, threads, n, slice, lane, index)221processBlockXOR(&B[offset], &B[prev], &B[newOffset])222index, offset = index+1, offset+1223}224wg.Done()225}226
227for n := uint32(0); n < time; n++ {228for slice := uint32(0); slice < syncPoints; slice++ {229var wg sync.WaitGroup230for lane := uint32(0); lane < threads; lane++ {231wg.Add(1)232go processSegment(n, slice, lane, &wg)233}234wg.Wait()235}236}237
238}
239
240func extractKey(B []block, memory, threads, keyLen uint32) []byte {241lanes := memory / threads242for lane := uint32(0); lane < threads-1; lane++ {243for i, v := range B[(lane*lanes)+lanes-1] {244B[memory-1][i] ^= v245}246}247
248var block [1024]byte249for i, v := range B[memory-1] {250binary.LittleEndian.PutUint64(block[i*8:], v)251}252key := make([]byte, keyLen)253blake2bHash(key, block[:])254return key255}
256
257func indexAlpha(rand uint64, lanes, segments, threads, n, slice, lane, index uint32) uint32 {258refLane := uint32(rand>>32) % threads259if n == 0 && slice == 0 {260refLane = lane261}262m, s := 3*segments, ((slice+1)%syncPoints)*segments263if lane == refLane {264m += index265}266if n == 0 {267m, s = slice*segments, 0268if slice == 0 || lane == refLane {269m += index270}271}272if index == 0 || lane == refLane {273m--274}275return phi(rand, uint64(m), uint64(s), refLane, lanes)276}
277
278func phi(rand, m, s uint64, lane, lanes uint32) uint32 {279p := rand & 0xFFFFFFFF280p = (p * p) >> 32281p = (p * m) >> 32282return lane*lanes + uint32((s+m-(p+1))%uint64(lanes))283}
284