From e500860fdbc2e5d612b9a2cdf0b919f28009a6b3 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Sat, 6 Nov 2021 00:49:20 +0100 Subject: [PATCH 1/4] Compatibility with RustCrypto --- balloon.go | 29 ++++++++++++++-------- balloon_test.go | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/balloon.go b/balloon.go index 5d81aee..0a65a37 100644 --- a/balloon.go +++ b/balloon.go @@ -43,7 +43,7 @@ func BalloonM(hr func() hash.Hash, passphrase, salt []byte, sCost, tCost uint64, for m := uint64(0); m < M; m++ { go func(core uint64) { binaryM := make([]byte, 8) - binary.BigEndian.PutUint64(binaryM, core) + binary.LittleEndian.PutUint64(binaryM, core) bouts <- Balloon(hr(), passphrase, append(salt, binaryM...), sCost, tCost) }(m + 1) } @@ -75,7 +75,7 @@ func (b *Instance) Expand(h hash.Hash, passphrase, salt []byte, sCost uint64) { panic("balloon: internal buffer has wrong length") } h.Reset() - binary.Write(h, binary.BigEndian, b.Cnt) + binary.Write(h, binary.LittleEndian, b.Cnt) b.Cnt++ h.Write(passphrase) h.Write(salt) @@ -84,7 +84,7 @@ func (b *Instance) Expand(h hash.Hash, passphrase, salt []byte, sCost uint64) { for m := uint64(1); m < sCost; m++ { h.Reset() - binary.Write(h, binary.BigEndian, b.Cnt) + binary.Write(h, binary.LittleEndian, b.Cnt) h.Write(b.LastBlock) b.LastBlock = h.Sum(nil) copy(b.Buffer[b.Cnt*blockSize:], b.LastBlock) @@ -106,7 +106,7 @@ func (b *Instance) Mix(h hash.Hash, salt []byte, sCost, tCost uint64) { for t := uint64(0); t < tCost; t++ { for m := uint64(0); m < sCost; m++ { h.Reset() - binary.Write(h, binary.BigEndian, b.Cnt) + binary.Write(h, binary.LittleEndian, b.Cnt) b.Cnt++ h.Write(b.LastBlock) h.Write(b.Buffer[m*blockSize : (m+1)*blockSize]) @@ -115,17 +115,26 @@ func (b *Instance) Mix(h hash.Hash, salt []byte, sCost, tCost uint64) { for i := uint64(0); i < delta; i++ { h.Reset() - binary.Write(h, binary.BigEndian, b.Cnt) + binary.Write(h, binary.LittleEndian, t) + binary.Write(h, binary.LittleEndian, m) + binary.Write(h, binary.LittleEndian, i) + idxBlock := h.Sum(nil) + h.Reset() + binary.Write(h, binary.LittleEndian, b.Cnt) b.Cnt++ h.Write(salt) - binary.Write(h, binary.BigEndian, t) - binary.Write(h, binary.BigEndian, m) - binary.Write(h, binary.BigEndian, i) - otherInt.SetBytes(h.Sum(nil)) + h.Write(idxBlock) + otherBytes := h.Sum(nil) + + for left, right := 0, len(otherBytes)-1; left < right; left, right = left+1, right-1 { + otherBytes[left], otherBytes[right] = otherBytes[right], otherBytes[left] + } + + otherInt.SetBytes(otherBytes) otherInt.Mod(otherInt, sCostInt) other := otherInt.Uint64() h.Reset() - binary.Write(h, binary.BigEndian, b.Cnt) + binary.Write(h, binary.LittleEndian, b.Cnt) b.Cnt++ h.Write(b.LastBlock) h.Write(b.Buffer[other*blockSize : (other+1)*blockSize]) diff --git a/balloon_test.go b/balloon_test.go index eebb467..ce9f3e0 100644 --- a/balloon_test.go +++ b/balloon_test.go @@ -9,7 +9,10 @@ package balloon import ( "crypto/rand" + "crypto/sha256" "crypto/sha512" + "encoding/hex" + "reflect" "testing" ) @@ -30,3 +33,65 @@ func BenchmarkBalloonM(b *testing.B) { BalloonM(sha512.New, ps[:8], ps[8:], 16, 16, 4) } } + +func TestVectors(t *testing.T) { + type test_vector struct { + password []byte + salt []byte + s_cost uint64 + t_cost uint64 + output string + } + + test_vectors := []test_vector{ + { + password: []byte("hunter42"), + salt: []byte("examplesalt"), + s_cost: 1024, + t_cost: 3, + output: "716043dff777b44aa7b88dcbab12c078abecfac9d289c5b5195967aa63440dfb", + }, + { + password: []byte(""), + salt: []byte("salt"), + s_cost: 3, + t_cost: 3, + output: "5f02f8206f9cd212485c6bdf85527b698956701ad0852106f94b94ee94577378", + }, + { + password: []byte("password"), + salt: []byte(""), + s_cost: 3, + t_cost: 3, + output: "20aa99d7fe3f4df4bd98c655c5480ec98b143107a331fd491deda885c4d6a6cc", + }, + { + password: []byte("\000"), + salt: []byte("\000"), + s_cost: 3, + t_cost: 3, + output: "4fc7e302ffa29ae0eac31166cee7a552d1d71135f4e0da66486fb68a749b73a4", + }, + { + password: []byte("password"), + salt: []byte("salt"), + s_cost: 1, + t_cost: 1, + output: "eefda4a8a75b461fa389c1dcfaf3e9dfacbc26f81f22e6f280d15cc18c417545", + }, + } + + for _, vector := range test_vectors { + output := Balloon(sha256.New(), vector.password, vector.salt, vector.s_cost, vector.t_cost) + data, err := hex.DecodeString(vector.output) + + if err != nil { + panic(err) + } + + if !reflect.DeepEqual(output, data) { + t.Log("Expected: ", vector.output, " Output: ", hex.EncodeToString(output)) + t.Fail() + } + } +} From 37a3c6d3d424532862e06fe20a54288f751760f2 Mon Sep 17 00:00:00 2001 From: daxpedda Date: Mon, 8 Nov 2021 12:51:41 +0100 Subject: [PATCH 2/4] Add test-vectors for both variants --- balloon_test.go | 94 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 93 insertions(+), 1 deletion(-) diff --git a/balloon_test.go b/balloon_test.go index ce9f3e0..253b2f9 100644 --- a/balloon_test.go +++ b/balloon_test.go @@ -34,7 +34,7 @@ func BenchmarkBalloonM(b *testing.B) { } } -func TestVectors(t *testing.T) { +func TestBalloonVectors(t *testing.T) { type test_vector struct { password []byte salt []byte @@ -95,3 +95,95 @@ func TestVectors(t *testing.T) { } } } + +func TestBalloonMVectors(t *testing.T) { + type test_vector struct { + password []byte + salt []byte + s_cost uint64 + t_cost uint64 + p_cost uint64 + output string + } + + test_vectors := []test_vector{ + { + password: []byte("hunter42"), + salt: []byte("examplesalt"), + s_cost: 1024, + t_cost: 3, + p_cost: 4, + output: "1832bd8e5cbeba1cb174a13838095e7e66508e9bf04c40178990adbc8ba9eb6f", + }, + { + password: []byte(""), + salt: []byte("salt"), + s_cost: 3, + t_cost: 3, + p_cost: 2, + output: "f8767fe04059cef67b4427cda99bf8bcdd983959dbd399a5e63ea04523716c23", + }, + { + password: []byte("password"), + salt: []byte(""), + s_cost: 3, + t_cost: 3, + p_cost: 3, + output: "bcad257eff3d1090b50276514857e60db5d0ec484129013ef3c88f7d36e438d6", + }, + { + password: []byte("password"), + salt: []byte(""), + s_cost: 3, + t_cost: 3, + p_cost: 1, + output: "498344ee9d31baf82cc93ebb3874fe0b76e164302c1cefa1b63a90a69afb9b4d", + }, + { + password: []byte("\000"), + salt: []byte("\000"), + s_cost: 3, + t_cost: 3, + p_cost: 4, + output: "8a665611e40710ba1fd78c181549c750f17c12e423c11930ce997f04c7153e0c", + }, + { + password: []byte("\000"), + salt: []byte("\000"), + s_cost: 3, + t_cost: 3, + p_cost: 1, + output: "d9e33c683451b21fb3720afbd78bf12518c1d4401fa39f054b052a145c968bb1", + }, + { + password: []byte("password"), + salt: []byte("salt"), + s_cost: 1, + t_cost: 1, + p_cost: 16, + output: "a67b383bb88a282aef595d98697f90820adf64582a4b3627c76b7da3d8bae915", + }, + { + password: []byte("password"), + salt: []byte("salt"), + s_cost: 1, + t_cost: 1, + p_cost: 1, + output: "97a11df9382a788c781929831d409d3599e0b67ab452ef834718114efdcd1c6d", + }, + } + + for _, vector := range test_vectors { + output := BalloonM(sha256.New, vector.password, vector.salt, vector.s_cost, vector.t_cost, vector.p_cost) + data, err := hex.DecodeString(vector.output) + + if err != nil { + panic(err) + } + + if !reflect.DeepEqual(output, data) { + t.Log("Expected: ", vector.output, " Output: ", hex.EncodeToString(output)) + t.Fail() + } + } +} From e2e1e305e9840d0fc4e76a5f52dd79066c5ea155 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Wed, 22 Jun 2022 13:38:31 +0200 Subject: [PATCH 3/4] Remove reversing bits --- balloon.go | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/balloon.go b/balloon.go index 0a65a37..9017465 100644 --- a/balloon.go +++ b/balloon.go @@ -11,6 +11,7 @@ import ( "encoding/binary" "hash" "math/big" + "math/bits" ) const ( @@ -125,12 +126,19 @@ func (b *Instance) Mix(h hash.Hash, salt []byte, sCost, tCost uint64) { h.Write(salt) h.Write(idxBlock) otherBytes := h.Sum(nil) + otherWords := make([]big.Word, h.Size() / (bits.UintSize / 8)) - for left, right := 0, len(otherBytes)-1; left < right; left, right = left+1, right-1 { - otherBytes[left], otherBytes[right] = otherBytes[right], otherBytes[left] + for byte, word := 0, 0; byte < len(otherBytes); byte, word = byte+bits.UintSize/8, word+1 { + if bits.UintSize == 64 { + otherWords[word] = big.Word(binary.LittleEndian.Uint64(otherBytes[byte : byte+bits.UintSize/8])) + } else if bits.UintSize == 32 { + otherWords[word] = big.Word(binary.LittleEndian.Uint32(otherBytes[byte : byte+bits.UintSize/8])) + } else { + panic("balloon: unsupported architecture") + } } - otherInt.SetBytes(otherBytes) + otherInt.SetBits(otherWords) otherInt.Mod(otherInt, sCostInt) other := otherInt.Uint64() h.Reset() From f8ff47299ff2ac7ebf2475c9dbe74382d2843e68 Mon Sep 17 00:00:00 2001 From: dAxpeDDa Date: Wed, 22 Jun 2022 13:52:00 +0200 Subject: [PATCH 4/4] Format code --- balloon.go | 2 +- balloon_test.go | 154 ++++++++++++++++++++++++------------------------ 2 files changed, 78 insertions(+), 78 deletions(-) diff --git a/balloon.go b/balloon.go index 9017465..84d6ff0 100644 --- a/balloon.go +++ b/balloon.go @@ -126,7 +126,7 @@ func (b *Instance) Mix(h hash.Hash, salt []byte, sCost, tCost uint64) { h.Write(salt) h.Write(idxBlock) otherBytes := h.Sum(nil) - otherWords := make([]big.Word, h.Size() / (bits.UintSize / 8)) + otherWords := make([]big.Word, h.Size()/(bits.UintSize/8)) for byte, word := 0, 0; byte < len(otherBytes); byte, word = byte+bits.UintSize/8, word+1 { if bits.UintSize == 64 { diff --git a/balloon_test.go b/balloon_test.go index 253b2f9..ddbd5b3 100644 --- a/balloon_test.go +++ b/balloon_test.go @@ -35,54 +35,54 @@ func BenchmarkBalloonM(b *testing.B) { } func TestBalloonVectors(t *testing.T) { - type test_vector struct { + type testVector struct { password []byte - salt []byte - s_cost uint64 - t_cost uint64 - output string + salt []byte + sCost uint64 + tCost uint64 + output string } - test_vectors := []test_vector{ + testVectors := []testVector{ { password: []byte("hunter42"), - salt: []byte("examplesalt"), - s_cost: 1024, - t_cost: 3, - output: "716043dff777b44aa7b88dcbab12c078abecfac9d289c5b5195967aa63440dfb", + salt: []byte("examplesalt"), + sCost: 1024, + tCost: 3, + output: "716043dff777b44aa7b88dcbab12c078abecfac9d289c5b5195967aa63440dfb", }, { password: []byte(""), - salt: []byte("salt"), - s_cost: 3, - t_cost: 3, - output: "5f02f8206f9cd212485c6bdf85527b698956701ad0852106f94b94ee94577378", + salt: []byte("salt"), + sCost: 3, + tCost: 3, + output: "5f02f8206f9cd212485c6bdf85527b698956701ad0852106f94b94ee94577378", }, { password: []byte("password"), - salt: []byte(""), - s_cost: 3, - t_cost: 3, - output: "20aa99d7fe3f4df4bd98c655c5480ec98b143107a331fd491deda885c4d6a6cc", + salt: []byte(""), + sCost: 3, + tCost: 3, + output: "20aa99d7fe3f4df4bd98c655c5480ec98b143107a331fd491deda885c4d6a6cc", }, { password: []byte("\000"), - salt: []byte("\000"), - s_cost: 3, - t_cost: 3, - output: "4fc7e302ffa29ae0eac31166cee7a552d1d71135f4e0da66486fb68a749b73a4", + salt: []byte("\000"), + sCost: 3, + tCost: 3, + output: "4fc7e302ffa29ae0eac31166cee7a552d1d71135f4e0da66486fb68a749b73a4", }, { password: []byte("password"), - salt: []byte("salt"), - s_cost: 1, - t_cost: 1, - output: "eefda4a8a75b461fa389c1dcfaf3e9dfacbc26f81f22e6f280d15cc18c417545", + salt: []byte("salt"), + sCost: 1, + tCost: 1, + output: "eefda4a8a75b461fa389c1dcfaf3e9dfacbc26f81f22e6f280d15cc18c417545", }, } - for _, vector := range test_vectors { - output := Balloon(sha256.New(), vector.password, vector.salt, vector.s_cost, vector.t_cost) + for _, vector := range testVectors { + output := Balloon(sha256.New(), vector.password, vector.salt, vector.sCost, vector.tCost) data, err := hex.DecodeString(vector.output) if err != nil { @@ -97,84 +97,84 @@ func TestBalloonVectors(t *testing.T) { } func TestBalloonMVectors(t *testing.T) { - type test_vector struct { + type testVector struct { password []byte - salt []byte - s_cost uint64 - t_cost uint64 - p_cost uint64 - output string + salt []byte + sCost uint64 + tCost uint64 + pCost uint64 + output string } - test_vectors := []test_vector{ + testVectors := []testVector{ { password: []byte("hunter42"), - salt: []byte("examplesalt"), - s_cost: 1024, - t_cost: 3, - p_cost: 4, - output: "1832bd8e5cbeba1cb174a13838095e7e66508e9bf04c40178990adbc8ba9eb6f", + salt: []byte("examplesalt"), + sCost: 1024, + tCost: 3, + pCost: 4, + output: "1832bd8e5cbeba1cb174a13838095e7e66508e9bf04c40178990adbc8ba9eb6f", }, { password: []byte(""), - salt: []byte("salt"), - s_cost: 3, - t_cost: 3, - p_cost: 2, - output: "f8767fe04059cef67b4427cda99bf8bcdd983959dbd399a5e63ea04523716c23", + salt: []byte("salt"), + sCost: 3, + tCost: 3, + pCost: 2, + output: "f8767fe04059cef67b4427cda99bf8bcdd983959dbd399a5e63ea04523716c23", }, { password: []byte("password"), - salt: []byte(""), - s_cost: 3, - t_cost: 3, - p_cost: 3, - output: "bcad257eff3d1090b50276514857e60db5d0ec484129013ef3c88f7d36e438d6", + salt: []byte(""), + sCost: 3, + tCost: 3, + pCost: 3, + output: "bcad257eff3d1090b50276514857e60db5d0ec484129013ef3c88f7d36e438d6", }, { password: []byte("password"), - salt: []byte(""), - s_cost: 3, - t_cost: 3, - p_cost: 1, - output: "498344ee9d31baf82cc93ebb3874fe0b76e164302c1cefa1b63a90a69afb9b4d", + salt: []byte(""), + sCost: 3, + tCost: 3, + pCost: 1, + output: "498344ee9d31baf82cc93ebb3874fe0b76e164302c1cefa1b63a90a69afb9b4d", }, { password: []byte("\000"), - salt: []byte("\000"), - s_cost: 3, - t_cost: 3, - p_cost: 4, - output: "8a665611e40710ba1fd78c181549c750f17c12e423c11930ce997f04c7153e0c", + salt: []byte("\000"), + sCost: 3, + tCost: 3, + pCost: 4, + output: "8a665611e40710ba1fd78c181549c750f17c12e423c11930ce997f04c7153e0c", }, { password: []byte("\000"), - salt: []byte("\000"), - s_cost: 3, - t_cost: 3, - p_cost: 1, - output: "d9e33c683451b21fb3720afbd78bf12518c1d4401fa39f054b052a145c968bb1", + salt: []byte("\000"), + sCost: 3, + tCost: 3, + pCost: 1, + output: "d9e33c683451b21fb3720afbd78bf12518c1d4401fa39f054b052a145c968bb1", }, { password: []byte("password"), - salt: []byte("salt"), - s_cost: 1, - t_cost: 1, - p_cost: 16, - output: "a67b383bb88a282aef595d98697f90820adf64582a4b3627c76b7da3d8bae915", + salt: []byte("salt"), + sCost: 1, + tCost: 1, + pCost: 16, + output: "a67b383bb88a282aef595d98697f90820adf64582a4b3627c76b7da3d8bae915", }, { password: []byte("password"), - salt: []byte("salt"), - s_cost: 1, - t_cost: 1, - p_cost: 1, - output: "97a11df9382a788c781929831d409d3599e0b67ab452ef834718114efdcd1c6d", + salt: []byte("salt"), + sCost: 1, + tCost: 1, + pCost: 1, + output: "97a11df9382a788c781929831d409d3599e0b67ab452ef834718114efdcd1c6d", }, } - for _, vector := range test_vectors { - output := BalloonM(sha256.New, vector.password, vector.salt, vector.s_cost, vector.t_cost, vector.p_cost) + for _, vector := range testVectors { + output := BalloonM(sha256.New, vector.password, vector.salt, vector.sCost, vector.tCost, vector.pCost) data, err := hex.DecodeString(vector.output) if err != nil {