From: Sergey Matveev Date: Sat, 20 May 2023 17:42:08 +0000 (+0300) Subject: Compatible results with other implementations X-Git-Tag: v2.0.0^0 X-Git-Url: http://www.git.cypherpunks.ru/?p=balloon.git;a=commitdiff_plain;h=362faad185dcdc9ad6039e873d0a06de48540997 Compatible results with other implementations --- diff --git a/balloon.go b/balloon.go index 8f296b7..9d34db8 100644 --- a/balloon.go +++ b/balloon.go @@ -27,37 +27,35 @@ import ( "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) @@ -65,7 +63,7 @@ func B(h hash.Hash, passwd, salt []byte, sCost, tCost uint64) []byte { 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) @@ -87,7 +85,7 @@ func B(h hash.Hash, passwd, salt []byte, sCost, tCost uint64) []byte { } else { prev = buf[(m-1)*hSize : m*hSize] } - binary.BigEndian.PutUint64(intBuf, cnt) + binary.LittleEndian.PutUint64(intBuf, cnt) cnt++ h.Reset() h.Write(intBuf) @@ -97,21 +95,27 @@ func B(h hash.Hash, passwd, salt []byte, sCost, tCost uint64) []byte { // 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) @@ -130,11 +134,11 @@ func B(h hash.Hash, passwd, salt []byte, sCost, tCost uint64) []byte { // 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) @@ -142,7 +146,7 @@ func H(hasher func() hash.Hash, passwd, salt []byte, sCost, tCost int, jobs int) 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) } diff --git a/balloon_test.go b/balloon_test.go index bd3ffec..87caf20 100644 --- a/balloon_test.go +++ b/balloon_test.go @@ -19,8 +19,11 @@ License along with this program. If not, see package balloon import ( + "bytes" "crypto/rand" + "crypto/sha256" "crypto/sha512" + "encoding/hex" "testing" "testing/quick" ) @@ -75,3 +78,49 @@ func BenchmarkH(b *testing.B) { 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() + } +} diff --git a/cmd/balloon/main.go b/cmd/balloon/main.go index b22924c..d1a5314 100644 --- a/cmd/balloon/main.go +++ b/cmd/balloon/main.go @@ -28,7 +28,7 @@ import ( "io" "os" - "go.cypherpunks.ru/balloon" + "go.cypherpunks.ru/balloon/v2" ) func main() { diff --git a/go.mod b/go.mod index 1496b6c..8492546 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,3 @@ -module go.cypherpunks.ru/balloon +module go.cypherpunks.ru/balloon/v2 go 1.12