mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 17:52:25 -05:00 
			
		
		
		
	
		
			
	
	
		
			284 lines
		
	
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			284 lines
		
	
	
	
		
			9.2 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // 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)) | ||
|  | } |