"math/big"
)
-const (
- delta = 3
-)
+const delta = 3
// This function takes hash, password, salt, space cost (buffer size,
// number of hash-output sized blocks), time cost (number of rounds) and
// performs the following:
//
-// # Expand input into buffer.
-// buf[0] = hash(cnt++ || passwd || salt)
-// for m from 1 to sCost-1:
-// buf[m] = hash(cnt++ || buf[m-1])
-// # Mix buffer contents.
-// for t from 0 to tCost-1:
-// for m from 0 to sCost-1:
-// # Hash last and current blocks.
-// prev = buf[(m-1) mod sCost]
-// buf[m] = hash(cnt++ || prev || buf[m])
-// # Hash in pseudorandomly chosen blocks.
-// for i from 0 to delta-1:
-// other = to_int(hash(cnt++ || salt || t || m || i)) mod sCost
-// buf[m] = hash(cnt++ || buf[m] || buf[other])
-// # Extract output from buffer.
-// return buf[sCost-1]
+// # Expand input into buffer.
+// buf[0] = hash(cnt++ || passwd || salt)
+// for m from 1 to sCost-1:
+// buf[m] = hash(cnt++ || buf[m-1])
+// # Mix buffer contents.
+// for t from 0 to tCost-1:
+// for m from 0 to sCost-1:
+// # Hash last and current blocks.
+// prev = buf[(m-1) mod sCost]
+// buf[m] = hash(cnt++ || prev || buf[m])
+// # Hash in pseudorandomly chosen blocks.
+// for i from 0 to delta-1:
+// other = to_int(hash(cnt++ || salt || t || m || i)) mod sCost
+// buf[m] = hash(cnt++ || buf[m] || buf[other])
+// # Extract output from buffer.
+// return buf[sCost-1]
func B(h hash.Hash, passwd, salt []byte, sCost, tCost uint64) []byte {
var cnt uint64
intBuf := make([]byte, 8)
hSize := uint64(h.Size())
buf := make([]byte, 0, sCost*hSize)
// Expand input into buffer
- binary.BigEndian.PutUint64(intBuf, cnt)
+ binary.LittleEndian.PutUint64(intBuf, cnt)
cnt++
h.Write(intBuf)
h.Write(passwd)
buf = h.Sum(buf)
var m uint64
for m = 1; m < sCost; m++ {
- binary.BigEndian.PutUint64(intBuf, cnt)
+ binary.LittleEndian.PutUint64(intBuf, cnt)
cnt++
h.Reset()
h.Write(intBuf)
} else {
prev = buf[(m-1)*hSize : m*hSize]
}
- binary.BigEndian.PutUint64(intBuf, cnt)
+ binary.LittleEndian.PutUint64(intBuf, cnt)
cnt++
h.Reset()
h.Write(intBuf)
// Hash in pseudorandomly chosen blocks
for i = 0; i < delta; i++ {
- binary.BigEndian.PutUint64(intBuf, cnt)
- cnt++
h.Reset()
+ binary.LittleEndian.PutUint64(intBuf, t)
h.Write(intBuf)
- h.Write(salt)
- binary.BigEndian.PutUint64(intBuf, t)
+ binary.LittleEndian.PutUint64(intBuf, m)
h.Write(intBuf)
- binary.BigEndian.PutUint64(intBuf, m)
+ binary.LittleEndian.PutUint64(intBuf, i)
h.Write(intBuf)
- binary.BigEndian.PutUint64(intBuf, i)
+ biBuf = h.Sum(biBuf[:0])
+ binary.LittleEndian.PutUint64(intBuf, cnt)
+ cnt++
+ h.Reset()
h.Write(intBuf)
+ h.Write(salt)
+ h.Write(biBuf)
biBuf = h.Sum(biBuf[:0])
+ for i, j := 0, len(biBuf)-1; i < j; i, j = i+1, j-1 {
+ biBuf[i], biBuf[j] = biBuf[j], biBuf[i]
+ }
bi.SetBytes(biBuf)
bi.Mod(bi, bs)
- binary.BigEndian.PutUint64(intBuf, cnt)
+ binary.LittleEndian.PutUint64(intBuf, cnt)
cnt++
h.Reset()
h.Write(intBuf)
// run several hashers (jobs) simultaneously and second-preimage resistant
// password double hashing.
//
-// H(p, s, jobs) = hash(p || s || (
-// B(p, s || "1") XOR
-// B(p, s || "2") XOR
-// B(p, s || jobs)
-// ))
+// H(p, s, jobs) = hash(p || s || (
+// B(p, s || "1") XOR
+// B(p, s || "2") XOR
+// B(p, s || jobs)
+// ))
func H(hasher func() hash.Hash, passwd, salt []byte, sCost, tCost int, jobs int) []byte {
var i int
results := make(chan []byte)
go func(i int) {
saltBuf := make([]byte, len(salt)+8)
copy(saltBuf, salt)
- binary.BigEndian.PutUint64(saltBuf[len(salt):], uint64(i))
+ binary.LittleEndian.PutUint64(saltBuf[len(salt):], uint64(i))
results <- B(hasher(), passwd, saltBuf, uint64(sCost), uint64(tCost))
}(i)
}
package balloon
import (
+ "bytes"
"crypto/rand"
+ "crypto/sha256"
"crypto/sha512"
+ "encoding/hex"
"testing"
"testing/quick"
)
H(sha512.New, passwd, salt, sCost, 4, 4)
}
}
+
+func mustHexDecode(s string) []byte {
+ b, err := hex.DecodeString(s)
+ if err != nil {
+ panic(err)
+ }
+ return b
+}
+
+func TestVectors(t *testing.T) {
+ // taken from Nettle 3.9
+ if !bytes.Equal(
+ B(sha256.New(), []byte("password"), []byte("salt"), 1, 1),
+ mustHexDecode("eefda4a8a75b461fa389c1dcfaf3e9dfacbc26f81f22e6f280d15cc18c417545"),
+ ) {
+ t.FailNow()
+ }
+
+ if !bytes.Equal(
+ B(sha256.New(), []byte{0}, []byte{0}, 3, 3),
+ mustHexDecode("4fc7e302ffa29ae0eac31166cee7a552d1d71135f4e0da66486fb68a749b73a4"),
+ ) {
+ t.FailNow()
+ }
+
+ if !bytes.Equal(
+ B(sha256.New(), nil, []byte("salt"), 3, 3),
+ mustHexDecode("5f02f8206f9cd212485c6bdf85527b698956701ad0852106f94b94ee94577378"),
+ ) {
+ t.FailNow()
+ }
+
+ if !bytes.Equal(
+ B(sha256.New(), []byte("password"), nil, 3, 3),
+ mustHexDecode("20aa99d7fe3f4df4bd98c655c5480ec98b143107a331fd491deda885c4d6a6cc"),
+ ) {
+ t.FailNow()
+ }
+
+ if !bytes.Equal(
+ B(sha256.New(), []byte("hunter42"), []byte("examplesalt"), 1024, 3),
+ mustHexDecode("716043dff777b44aa7b88dcbab12c078abecfac9d289c5b5195967aa63440dfb"),
+ ) {
+ t.FailNow()
+ }
+}