1
0
mirror of https://github.com/taigrr/arc synced 2025-01-18 04:33:13 -08:00
2016-05-24 21:26:50 +09:00

303 lines
6.0 KiB
Go

package argon2
import (
"hash"
"testing"
"github.com/dchest/blake2b"
)
const version uint32 = 0x13
const mode = 0 // Argon2d
/*
inputs:
P message
S nonce
K secret key (optional)
X associated data (optional)
p parallelism
m memory size
n iterations
*/
func argon2(output, P, S, K, X []byte, p, m, n uint32, t *testing.T) {
if p == 0 || m == 0 || n == 0 {
panic("argon: internal error: invalid params")
}
if m%(p*4) != 0 {
panic("argon: internal error: invalid m")
}
m0 := m
if m < 8*p {
m = 8 * p
}
// Argon2 operates over a matrix of 1024-byte blocks
b := make([][128]uint64, m)
q := m / p // length of each lane
g := q / 4 // length of each segment
var scratch [72]byte
var btmp [1024]byte
var btmp2 [128]uint64
// Compute a hash of all the input parameters
h := blake2b.New512()
lh := newLongHash(h)
put32(scratch[0:4], p)
put32(scratch[4:8], uint32(len(output)))
put32(scratch[8:12], m0)
put32(scratch[12:16], n)
put32(scratch[16:20], version)
put32(scratch[20:24], mode)
h.Write(scratch[:24])
put32(scratch[0:4], uint32(len(P)))
h.Write(scratch[0:4])
h.Write(P)
put32(scratch[0:4], uint32(len(S)))
h.Write(scratch[0:4])
h.Write(S)
put32(scratch[0:4], uint32(len(K)))
h.Write(scratch[0:4])
h.Write(K)
put32(scratch[0:4], uint32(len(X)))
h.Write(scratch[0:4])
h.Write(X)
h.Sum(scratch[:0])
h.Reset()
// Use the hash to initialize the first two columns of the matrix
for lane := uint32(0); lane < p; lane++ {
// scratch[0:64] is the parameter hash
put32(scratch[64:], 0)
put32(scratch[68:], lane)
lh.Init(len(btmp))
lh.Write(scratch[:72])
lh.Hash(btmp[:])
for i := range b[0] {
b[lane*q+0][i] = read64(btmp[i*8:])
}
scratch[64] = 1
lh.Init(len(btmp))
lh.Write(scratch[:72])
lh.Hash(btmp[:])
for i := range b[0] {
b[lane*q+1][i] = read64(btmp[i*8:])
}
}
if t != nil {
t.Logf("Iterations: %d, Memory: %d KiB, Parallelism: %d lanes, Tag length: %d bytes", n, m, p, len(output))
t.Logf("Password[%d]: % x", len(P), P)
t.Logf("Nonce[%d]: % x", len(S), S)
t.Logf("Secret[%d]: % x", len(K), K)
t.Logf("Associated data[%d]: % x", len(X), X)
t.Logf("Input hash: % x", scratch[:64])
}
for i := range scratch {
scratch[i] = 0
}
for i := range btmp {
btmp[i] = 0
}
// Get down to business
for k := uint32(0); k < n; k++ {
if t != nil {
t.Log()
t.Logf(" After pass %d:", k)
}
for slice := uint32(0); slice < 4; slice++ {
for lane := uint32(0); lane < p; lane++ {
i := uint32(0)
if k == 0 && slice == 0 {
i = 2
}
j := lane*q + slice*g + i
for ; i < g; i, j = i+1, j+1 {
prev := j - 1
if i == 0 && slice == 0 {
prev = lane*q + q - 1
}
rand := b[prev][0]
rslice, rlane, ri := index(rand, q, g, p, k, slice, lane, i, t)
j0 := rlane*q + rslice*g + ri
block(&b[j], &btmp2, &b[prev], &b[j0])
}
}
}
if t != nil {
for i := range b {
t.Logf(" Block %.4d [0]: %x", i, b[i][0])
}
}
}
// XOR the blocks in the last column together
for lane := uint32(0); lane < p-1; lane++ {
for i, v := range b[lane*q+q-1] {
b[m-1][i] ^= v
}
}
// Output
for i, v := range b[m-1] {
btmp[i*8] = uint8(v)
btmp[i*8+1] = uint8(v >> 8)
btmp[i*8+2] = uint8(v >> 16)
btmp[i*8+3] = uint8(v >> 24)
btmp[i*8+4] = uint8(v >> 32)
btmp[i*8+5] = uint8(v >> 40)
btmp[i*8+6] = uint8(v >> 48)
btmp[i*8+7] = uint8(v >> 56)
}
if t != nil {
t.Logf("Final block: %x", btmp[:])
}
lh.Init(len(output))
lh.Write(btmp[:])
lh.Hash(output)
if t != nil {
t.Logf("Output: % X", output)
}
}
func index(rand uint64, q, g, p, k, slice, lane, i uint32, t *testing.T) (rslice, rlane, ri uint32) {
rlane = uint32(rand>>32) % p
var start, max uint32
if k == 0 {
start = 0
if slice == 0 || lane == rlane {
// All blocks in this lane so far
max = slice*g + i
} else {
// All blocks in another lane
// in slices prior to the current slice
max = slice * g
}
} else {
start = (slice + 1) % 4 * g
if lane == rlane {
// All blocks in this lane
max = 3*g + i
} else {
// All blocks in another lane
// except the current slice
max = 3 * g
}
}
if i == 0 || lane == rlane {
max -= 1
}
phi := rand & 0xFFFFFFFF
phi = phi * phi >> 32
phi = phi * uint64(max) >> 32
ri = uint32((uint64(start) + uint64(max) - 1 - phi) % uint64(q))
if t != nil {
i0 := lane*q + slice*g + i
j0 := rlane*q + ri
t.Logf(" i = %d(%d,%d,%d), rand = %d, max = %d, start = %d, phi = %d, j = %d(%d,%d,%d)", i0, lane, slice, i, rand, max, start, phi, j0, rlane, rslice, ri)
}
return rslice, rlane, ri
}
type longHash struct {
buf [64]uint8
h hash.Hash
h0 hash.Hash // large hash
h1 hash.Hash // small hash
n int
}
func newLongHash(h hash.Hash) *longHash {
return &longHash{h: h}
}
// Init readies longHash for an output of length n.
func (lh *longHash) Init(n int) {
lh.n = n
lh.h.Reset()
lh.h0 = lh.h
lh.h1 = lh.h
var err error
if n < 64 {
lh.h0, err = blake2b.New(&blake2b.Config{Size: uint8(n)})
} else if n%64 != 0 {
n := 33 + (n+31)%32
lh.h1, err = blake2b.New(&blake2b.Config{Size: uint8(n)})
}
if err != nil {
panic(err)
}
put32(lh.buf[:4], uint32(n))
lh.Write(lh.buf[:4])
}
func (lh *longHash) Write(b []byte) {
lh.h0.Write(b)
}
func (lh *longHash) Hash(out []byte) {
if len(out) != lh.n {
panic("argon2: wrong output length in longHash")
}
if len(out) <= 64 {
lh.h0.Sum(out[:0])
return
}
lh.h0.Sum(lh.buf[:0])
copy(out, lh.buf[:32])
for out = out[32:]; len(out) > 64; out = out[32:] {
lh.h0.Reset()
lh.h0.Write(lh.buf[:])
lh.h0.Sum(lh.buf[:0])
copy(out, lh.buf[:32])
}
if lh.h0 == lh.h1 {
lh.h1.Reset()
}
lh.h1.Write(lh.buf[:])
lh.h1.Sum(out[:0])
}
func put32(b []uint8, v uint32) {
b[0] = uint8(v)
b[1] = uint8(v >> 8)
b[2] = uint8(v >> 16)
b[3] = uint8(v >> 24)
}
func read64(b []uint8) uint64 {
return uint64(b[0]) |
uint64(b[1])<<8 |
uint64(b[2])<<16 |
uint64(b[3])<<24 |
uint64(b[4])<<32 |
uint64(b[5])<<40 |
uint64(b[6])<<48 |
uint64(b[7])<<56
}