1 /**
2 * PBKDF2
3 * 
4 * Copyright:
5 * (C) 1999-2007,2012 Jack Lloyd
6 * (C) 2014-2015 Etienne Cimon
7 *
8 * License:
9 * Botan is released under the Simplified BSD License (see LICENSE.md)
10 */
11 module botan.pbkdf.pbkdf2;
12 
13 import botan.constants;
14 static if (BOTAN_HAS_PBKDF2):
15 
16 import botan.pbkdf.pbkdf;
17 import botan.mac.mac;
18 import botan.utils.get_byte;
19 import botan.utils.xor_buf;
20 import botan.utils.rounding;
21 import std.datetime;
22 import std.conv : to;
23 import std.algorithm : min;
24 
25 /**
26 * PKCS #5 PBKDF2
27 */
28 final class PKCS5_PBKDF2 : PBKDF
29 {
30 public:
31     override @property string name() const
32     {
33         return "PBKDF2(" ~ m_mac.name ~ ")";
34     }
35 
36     override PBKDF clone() const
37     {
38         return new PKCS5_PBKDF2(m_mac.clone());
39     }
40 
41     /*
42     * Return a PKCS #5 PBKDF2 derived key
43     */
44     override 
45     Pair!(size_t, OctetString)
46         keyDerivation(size_t key_len,
47                       in string passphrase,
48                       const(ubyte)* salt, size_t salt_len,
49                       size_t iterations,
50                       Duration loop_for) const
51     {
52         if (key_len == 0)
53             return makePair(iterations, OctetString());
54         Unique!MessageAuthenticationCode mac = m_mac.clone();
55         try
56         {
57             mac.setKey(cast(const(ubyte)*)(passphrase.ptr), passphrase.length);
58         }
59         catch(InvalidKeyLength)
60         {
61             throw new Exception(name ~ " cannot accept passphrases of length " ~ to!string(passphrase.length));
62         }
63         
64         SecureVector!ubyte key = SecureVector!ubyte(key_len);
65         
66         ubyte* T = key.ptr;
67         
68         SecureVector!ubyte U = SecureVector!ubyte(mac.outputLength);
69         
70         const size_t blocks_needed = roundUp(key_len, mac.outputLength) / mac.outputLength;
71         
72         Duration dur_per_block = loop_for / blocks_needed;
73         
74         uint counter = 1;
75         while (key_len)
76         {
77             size_t T_size = min(mac.outputLength, key_len);
78             
79             mac.update(salt, salt_len);
80             mac.updateBigEndian(counter);
81             mac.flushInto(U.ptr);
82             
83             xorBuf(T, U.ptr, T_size);
84             
85             if (iterations == 0)
86             {
87                 /*
88                 If no iterations set, run the first block to calibrate based
89                 on how long hashing takes on whatever machine we're running on.
90                 */
91                 
92                 const auto start = Clock.currTime(UTC());
93                 
94                 iterations = 1; // the first iteration we did above
95                 
96                 while (true)
97                 {
98                     mac.update(U);
99                     mac.flushInto(U.ptr);
100                     xorBuf(T, U.ptr, T_size);
101                     iterations++;
102                     
103                     /*
104                     Only break on relatively 'even' iterations. For one it
105                     avoids confusion, and likely some broken implementations
106                     break on getting completely randomly distributed values
107                     */
108                     if (iterations % 10000 == 0)
109                     {
110                         auto time_taken = Clock.currTime(UTC()) - start;
111                         if (time_taken > dur_per_block)
112                             break;
113                     }
114                 }
115             }
116             else
117             {
118                 foreach (size_t i; 1 .. iterations)
119                 {
120                     mac.update(U);
121                     mac.flushInto(U.ptr);
122                     xorBuf(T, U.ptr, T_size);
123                 }
124             }
125             
126             key_len -= T_size;
127             T += T_size;
128             ++counter;
129         }
130         
131         return makePair(iterations, OctetString(key));
132     }
133 
134     /**
135     * Create a PKCS #5 instance using the specified message auth code
136     * Params:
137     *  mac_fn = the MAC object to use as PRF
138     */
139     this(MessageAuthenticationCode mac_fn) 
140     {
141         m_mac = mac_fn;
142     }
143 private:
144     Unique!MessageAuthenticationCode m_mac;
145 }