]> Cypherpunks.ru repositories - balloon.git/commitdiff
Compatible results with other implementations v2.0.0
authorSergey Matveev <stargrave@stargrave.org>
Sat, 20 May 2023 17:42:08 +0000 (20:42 +0300)
committerSergey Matveev <stargrave@stargrave.org>
Sat, 20 May 2023 17:42:59 +0000 (20:42 +0300)
balloon.go
balloon_test.go
cmd/balloon/main.go
go.mod

index 8f296b74ee8c6305af4364ec060407b4d17b5a51..9d34db8fd261f622cea969737fef97256e34e1a0 100644 (file)
@@ -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)
        }
index bd3ffec152f22ff6e271b0f62ce3078b32dd0f53..87caf2094a55cbcf7404608d374ef07c81f0ce70 100644 (file)
@@ -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()
+       }
+}
index b22924c4e4caa8dd4bdcc84f3b3bd725cf2a66e6..d1a53146314834cf21829273c39e491ff835652d 100644 (file)
@@ -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 1496b6c1d8689080c988aac744d15294a59f76d8..8492546e915b38010dd7c1bbc3cdd570884dac04 100644 (file)
--- a/go.mod
+++ b/go.mod
@@ -1,3 +1,3 @@
-module go.cypherpunks.ru/balloon
+module go.cypherpunks.ru/balloon/v2
 
 go 1.12