X-Git-Url: http://www.git.cypherpunks.ru/?p=balloon.git;a=blobdiff_plain;f=balloon.go;h=7ce73a7417b9daf90f05980d4b1ef1351fa4566f;hp=f8257c4a88f220c90656751f095ffa2deef06b69;hb=HEAD;hpb=92c161713a13cc78403468c09f1557eb7c4a3a4a diff --git a/balloon.go b/balloon.go index f8257c4..137ee21 100644 --- a/balloon.go +++ b/balloon.go @@ -1,21 +1,18 @@ -/* -balloon -- Balloon password hashing function -Copyright (C) 2016-2018 Sergey Matveev - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as -published by the Free Software Foundation, either version 3 of the -License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with this program. If not, see -. -*/ +// balloon -- Balloon password hashing function +// Copyright (C) 2016-2024 Sergey Matveev +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU Lesser General Public License as +// the Free Software Foundation, version 3 of the License. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this program. If not, see +// . // Balloon password hashing. // @@ -28,121 +25,127 @@ 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] -func B(h hash.Hash, passwd, salt []byte, sCost, tCost int) []byte { +// # 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) - buf := make([][]byte, sCost) + 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) h.Write(salt) - buf[0] = h.Sum(nil) - var m int + 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) - h.Write(buf[m-1]) - buf[m] = h.Sum(nil) + h.Write(buf[(m-1)*hSize : m*hSize]) + buf = h.Sum(buf) } // Mix buffer contents var prev []byte - var i int + var i uint64 + var neigh uint64 bi := big.NewInt(0) bs := big.NewInt(int64(sCost)) biBuf := make([]byte, 0, h.Size()) - var other int - for t := 0; t < tCost; t++ { + for t := uint64(0); t < tCost; t++ { for m = 0; m < sCost; m++ { // Hash last and current blocks if m == 0 { - prev = buf[len(buf)-1] + prev = buf[(sCost-1)*hSize:] } else { - prev = buf[m-1] + prev = buf[(m-1)*hSize : m*hSize] } - binary.BigEndian.PutUint64(intBuf, cnt) + binary.LittleEndian.PutUint64(intBuf, cnt) cnt++ h.Reset() h.Write(intBuf) h.Write(prev) - h.Write(buf[m]) - buf[m] = h.Sum(buf[m][:0]) + h.Write(buf[m*hSize : (m+1)*hSize]) + buf = h.Sum(buf[:m*hSize]) // 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, uint64(t)) + binary.LittleEndian.PutUint64(intBuf, m) h.Write(intBuf) - binary.BigEndian.PutUint64(intBuf, uint64(m)) + binary.LittleEndian.PutUint64(intBuf, i) h.Write(intBuf) - binary.BigEndian.PutUint64(intBuf, uint64(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) - other = int(bi.Uint64()) - binary.BigEndian.PutUint64(intBuf, cnt) + binary.LittleEndian.PutUint64(intBuf, cnt) cnt++ h.Reset() h.Write(intBuf) - h.Write(buf[m]) - h.Write(buf[other]) - buf[m] = h.Sum(buf[m][:0]) + h.Write(buf[m*hSize : (m+1)*hSize]) + neigh = bi.Uint64() + h.Write(buf[neigh*hSize : (neigh+1)*hSize]) + buf = h.Sum(buf[:m*hSize]) } } } // Extract output from buffer - return buf[sCost-1] + return buf[(sCost-1)*hSize:] } // This function adds additional functionality over pure B(): ability to // 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) for ; i < jobs; i++ { go func(i int) { - saltBuf := make([]byte, 8) - binary.BigEndian.PutUint64(saltBuf, uint64(i)) - results <- B(hasher(), passwd, append(salt, saltBuf...), sCost, tCost) + saltBuf := make([]byte, len(salt)+8) + copy(saltBuf, salt) + binary.LittleEndian.PutUint64(saltBuf[len(salt):], uint64(i)) + results <- B(hasher(), passwd, saltBuf, uint64(sCost), uint64(tCost)) }(i) } h := hasher()