2 balloon -- Balloon password hashing function
3 Copyright (C) 2016-2017 Sergey Matveev <stargrave@stargrave.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
19 // Balloon password hashing.
21 // Look https://crypto.stanford.edu/balloon/ for more description.
34 // This function takes hash, password, salt, space cost (buffer size,
35 // number of hash-output sized blocks), time cost (number of rounds) and
36 // performs the following:
38 // # Expand input into buffer.
39 // buf[0] = hash(cnt++ || passwd || salt)
40 // for m from 1 to sCost-1:
41 // buf[m] = hash(cnt++ || buf[m-1])
42 // # Mix buffer contents.
43 // for t from 0 to tCost-1:
44 // for m from 0 to sCost-1:
45 // # Hash last and current blocks.
46 // prev = buf[(m-1) mod sCost]
47 // buf[m] = hash(cnt++ || prev || buf[m])
48 // # Hash in pseudorandomly chosen blocks.
49 // for i from 0 to delta-1:
50 // other = to_int(hash(cnt++ || salt || t || m || i)) mod sCost
51 // buf[m] = hash(cnt++ || buf[m] || buf[other])
52 // # Extract output from buffer.
53 // return buf[sCost-1]
54 func B(h hash.Hash, passwd, salt []byte, sCost, tCost int) []byte {
56 intBuf := make([]byte, 8)
57 buf := make([][]byte, sCost)
58 // Expand input into buffer
59 binary.BigEndian.PutUint64(intBuf, cnt)
66 for m = 1; m < sCost; m++ {
67 binary.BigEndian.PutUint64(intBuf, cnt)
74 // Mix buffer contents
78 bs := big.NewInt(int64(sCost))
79 biBuf := make([]byte, 0, h.Size())
81 for t := 0; t < tCost; t++ {
82 for m = 0; m < sCost; m++ {
83 // Hash last and current blocks
85 prev = buf[len(buf)-1]
89 binary.BigEndian.PutUint64(intBuf, cnt)
95 buf[m] = h.Sum(buf[m][:0])
97 // Hash in pseudorandomly chosen blocks
98 for i = 0; i < delta; i++ {
99 binary.BigEndian.PutUint64(intBuf, cnt)
104 binary.BigEndian.PutUint64(intBuf, uint64(t))
106 binary.BigEndian.PutUint64(intBuf, uint64(m))
108 binary.BigEndian.PutUint64(intBuf, uint64(i))
110 biBuf = h.Sum(biBuf[:0])
113 other = int(bi.Uint64())
114 binary.BigEndian.PutUint64(intBuf, cnt)
120 buf[m] = h.Sum(buf[m][:0])
124 // Extract output from buffer
128 // This function adds additional functionality over pure B(): ability to
129 // run several hashers (jobs) simultaneously and second-preimage resistant
130 // password double hashing.
132 // H(p, s, jobs) = hash(p || s || (
133 // B(p, s || "1") XOR
134 // B(p, s || "2") XOR
137 func H(hasher func() hash.Hash, passwd, salt []byte, sCost, tCost int, jobs int) []byte {
139 results := make(chan []byte)
140 for ; i < jobs; i++ {
142 saltBuf := make([]byte, 8)
143 binary.BigEndian.PutUint64(saltBuf, uint64(i))
144 results <- B(hasher(), passwd, append(salt, saltBuf...), sCost, tCost)
150 result := make([]byte, h.Size())
151 for i = 0; i < jobs; i++ {
152 for n, e := range <-results {
158 return h.Sum(result[:0])