1// Package bcrypt_pbkdf implements OpenBSD's bcrypt_pbkdf(3)
2package bcrypt_pbkdf
3
4import (
5	"crypto/sha512"
6	"github.com/ebfe/bcrypt_pbkdf/blowfish"
7)
8
9//  derived from /usr/src/lib/libutil/bcrypt_pbkdf.c
10/*
11	$OpenBSD: bcrypt_pbkdf.c,v 1.6 2014/01/31 16:56:32 tedu Exp $
12
13	Copyright (c) 2013 Ted Unangst <tedu@openbsd.org>
14
15	Permission to use, copy, modify, and distribute this software for any
16	purpose with or without fee is hereby granted, provided that the above
17	copyright notice and this permission notice appear in all copies.
18
19	THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
20	WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
21	MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
22	ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
23	WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
24	ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
25	OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
26*/
27
28const (
29	bcryptBlocks   = 8
30	bcryptHashSize = 4 * bcryptBlocks
31	magic          = "OxychromaticBlowfishSwatDynamite"
32)
33
34func bcryptHash(bf *blowfish.Cipher, hpass, hsalt, out []byte) {
35	err := bf.InitSaltedCipher(hpass, hsalt)
36	if err != nil {
37		panic(err)
38	}
39
40	for i := 0; i < 64; i++ {
41		blowfish.ExpandKey(hsalt, bf)
42		blowfish.ExpandKey(hpass, bf)
43	}
44
45	copy(out, magic)
46
47	for i := 0; i < 64; i++ {
48		for j := 0; j < bcryptHashSize/blowfish.BlockSize; j++ {
49			bf.Encrypt(out[j*blowfish.BlockSize:], out[j*blowfish.BlockSize:])
50		}
51	}
52
53	for i := 0; i < len(out); i += 4 {
54		out[i+0], out[i+1], out[i+2], out[i+3] = out[i+3], out[i+2], out[i+1], out[i+0]
55	}
56}
57
58func bcryptPBKDF(password, salt []byte, rounds, keyLen int) []byte {
59	countsalt := make([]byte, 4)
60	hpass := make([]byte, sha512.Size)
61	hsalt := make([]byte, sha512.Size)
62	out := make([]byte, bcryptHashSize)
63	tmp := make([]byte, bcryptHashSize)
64	key := make([]byte, keyLen)
65	cipher := &blowfish.Cipher{}
66
67	stride := (keyLen + bcryptHashSize - 1) / bcryptHashSize
68	amt := (keyLen + stride - 1) / stride
69
70	sha := sha512.New()
71	sha.Write(password)
72	hpass = sha.Sum(hpass[:0])
73
74	for count := uint32(1); keyLen > 0; count++ {
75		sha.Reset()
76		sha.Write(salt)
77		countsalt[0] = byte(count >> 24)
78		countsalt[1] = byte(count >> 16)
79		countsalt[2] = byte(count >> 8)
80		countsalt[3] = byte(count)
81		sha.Write(countsalt)
82		hsalt = sha.Sum(hsalt[:0])
83
84		bcryptHash(cipher, hpass, hsalt, tmp)
85		copy(out, tmp)
86
87		for i := 1; i < rounds; i++ {
88			sha.Reset()
89			sha.Write(tmp)
90			hsalt = sha.Sum(hsalt[:0])
91			bcryptHash(cipher, hpass, hsalt, tmp)
92			for i := range out {
93				out[i] ^= tmp[i]
94			}
95		}
96
97		if amt > keyLen {
98			amt = keyLen
99		}
100
101		for i := 0; i < amt; i++ {
102			key[i*stride+(int(count)-1)] = out[i]
103		}
104		keyLen -= amt
105	}
106
107	return key
108}
109
110func Key(password, salt []byte, rounds, keyLen int) []byte {
111	return bcryptPBKDF(password, salt, rounds, keyLen)
112}
113