mirror of
https://github.com/Luzifer/cloudkeys-go.git
synced 2024-11-10 07:00:08 +00:00
Knut Ahlers
a1df72edc5
commitf0db1ff1f8
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 12:19:56 2017 +0100 Mark option as deprecated Signed-off-by: Knut Ahlers <knut@ahlers.me> commit9891df2a16
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 12:11:56 2017 +0100 Fix: Typo Signed-off-by: Knut Ahlers <knut@ahlers.me> commit836006de64
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 12:04:20 2017 +0100 Add new dependencies Signed-off-by: Knut Ahlers <knut@ahlers.me> commitd64fee60c8
Author: Knut Ahlers <knut@ahlers.me> Date: Sun Dec 24 11:55:52 2017 +0100 Replace insecure password hashing Prior this commit passwords were hashed with a static salt and using the SHA1 hashing function. This could lead to passwords being attackable in case someone gets access to the raw data stored inside the database. This commit introduces password hashing using bcrypt hashing function which addresses this issue. Old passwords are not automatically re-hashed as they are unknown. Replacing the old password scheme is not that easy and needs #10 to be solved. Therefore the old hashing scheme is kept for compatibility reason. Signed-off-by: Knut Ahlers <knut@ahlers.me> Signed-off-by: Knut Ahlers <knut@ahlers.me> closes #14 closes #15
219 lines
6.5 KiB
Go
219 lines
6.5 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.
|
|
// Argon2 is specfifed at https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
|
|
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 parallism 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, 4, 32*1024, 4, 32)`
|
|
//
|
|
// The recommended parameters for interactive logins as of 2017 are time=4, memory=32*1024.
|
|
// The number of threads can be adjusted to the numbers of available CPUs.
|
|
// 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 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)
|
|
}
|
|
|
|
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: paralisim degree too low")
|
|
}
|
|
mem := memory / (4 * uint32(threads)) * (4 * uint32(threads))
|
|
if mem < 8*uint32(threads) {
|
|
mem = 8 * uint32(threads)
|
|
}
|
|
B := initBlocks(password, salt, secret, data, time, mem, uint32(threads), keyLen, mode)
|
|
processBlocks(B, time, mem, uint32(threads), mode)
|
|
return extractKey(B, mem, uint32(threads), keyLen)
|
|
}
|
|
|
|
const blockLength = 128
|
|
|
|
type block [blockLength]uint64
|
|
|
|
func initBlocks(password, salt, key, data []byte, time, memory, threads, keyLen uint32, mode int) []block {
|
|
var (
|
|
block0 [1024]byte
|
|
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])
|
|
|
|
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[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[0] {
|
|
B[j+1][i] = binary.LittleEndian.Uint64(block0[i*8:])
|
|
}
|
|
}
|
|
return B
|
|
}
|
|
|
|
func processBlocks(B []block, time, memory, threads uint32, mode int) {
|
|
const syncPoints = 4
|
|
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 && n == 0 && slice < syncPoints/2) {
|
|
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 = lane*lanes + lanes - 1 // 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
|
|
|
|
m, s := 3*segments, (slice+1)%4*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))
|
|
}
|