forked from GhostPack/SharpDPAPI
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathPBKDF2.cs
More file actions
executable file
·181 lines (154 loc) · 8.06 KB
/
Copy pathPBKDF2.cs
File metadata and controls
executable file
·181 lines (154 loc) · 8.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
//Copyright (c) 2012 Josip Medved <jmedved@jmedved.com>
//2012-04-12: Initial version.
using System;
using System.Security.Cryptography;
using System.Text;
namespace PBKDF2 {
/// <summary>
/// Generic PBKDF2 implementation.
/// </summary>
/// <example>This sample shows how to initialize class with SHA-256 HMAC.
/// <code>
/// using (var hmac = new HMACSHA256()) {
/// var df = new Pbkdf2(hmac, "password", "salt");
/// var bytes = df.GetBytes();
/// }
/// </code>
/// </example>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pbkdf", Justification = "Spelling is correct.")]
public class Pbkdf2 {
/// <summary>
/// Creates new instance.
/// </summary>
/// <param name="algorithm">HMAC algorithm to use.</param>
/// <param name="password">The password used to derive the key.</param>
/// <param name="salt">The key salt used to derive the key.</param>
/// <param name="iterations">The number of iterations for the operation.</param>
/// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
public Pbkdf2(HMAC algorithm, Byte[] password, Byte[] salt, Int32 iterations) {
if (algorithm == null) { throw new ArgumentNullException("algorithm", "Algorithm cannot be null."); }
if (salt == null) { throw new ArgumentNullException("salt", "Salt cannot be null."); }
if (password == null) { throw new ArgumentNullException("password", "Password cannot be null."); }
this.Algorithm = algorithm;
this.Algorithm.Key = password;
this.Salt = salt;
this.IterationCount = iterations;
this.BlockSize = this.Algorithm.HashSize / 8;
this.BufferBytes = new byte[this.BlockSize];
}
/// <summary>
/// Creates new instance.
/// </summary>
/// <param name="algorithm">HMAC algorithm to use.</param>
/// <param name="password">The password used to derive the key.</param>
/// <param name="salt">The key salt used to derive the key.</param>
/// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
public Pbkdf2(HMAC algorithm, Byte[] password, Byte[] salt)
: this(algorithm, password, salt, 1000) {
}
/// <summary>
/// Creates new instance.
/// </summary>
/// <param name="algorithm">HMAC algorithm to use.</param>
/// <param name="password">The password used to derive the key.</param>
/// <param name="salt">The key salt used to derive the key.</param>
/// <param name="iterations">The number of iterations for the operation.</param>
/// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
public Pbkdf2(HMAC algorithm, String password, String salt, Int32 iterations) :
this(algorithm, UTF8Encoding.UTF8.GetBytes(password), UTF8Encoding.UTF8.GetBytes(salt), iterations) {
}
/// <summary>
/// Creates new instance.
/// </summary>
/// <param name="algorithm">HMAC algorithm to use.</param>
/// <param name="password">The password used to derive the key.</param>
/// <param name="salt">The key salt used to derive the key.</param>
/// <exception cref="System.ArgumentNullException">Algorithm cannot be null - Password cannot be null. -or- Salt cannot be null.</exception>
public Pbkdf2(HMAC algorithm, String password, String salt) :
this(algorithm, password, salt, 1000) {
}
private readonly int BlockSize;
private uint BlockIndex = 1;
private byte[] BufferBytes;
private int BufferStartIndex = 0;
private int BufferEndIndex = 0;
/// <summary>
/// Gets algorithm used for generating key.
/// </summary>
public HMAC Algorithm { get; private set; }
/// <summary>
/// Gets salt bytes.
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Byte array is proper return value in this case.")]
public Byte[] Salt { get; private set; }
/// <summary>
/// Gets iteration count.
/// </summary>
public Int32 IterationCount { get; private set; }
/// <summary>
/// Returns a pseudo-random key from a password, salt and iteration count.
/// </summary>
/// <param name="count">Number of bytes to return.</param>
/// <returns>Byte array.</returns>
///
public Byte[] GetBytes(int count, string algorithm = "sha512") {
byte[] result = new byte[count];
int resultOffset = 0;
int bufferCount = this.BufferEndIndex - this.BufferStartIndex;
if (bufferCount > 0) { //if there is some data in buffer
if (count < bufferCount) { //if there is enough data in buffer
Buffer.BlockCopy(this.BufferBytes, this.BufferStartIndex, result, 0, count);
this.BufferStartIndex += count;
return result;
}
Buffer.BlockCopy(this.BufferBytes, this.BufferStartIndex, result, 0, bufferCount);
this.BufferStartIndex = this.BufferEndIndex = 0;
resultOffset += bufferCount;
}
while (resultOffset < count) {
int needCount = count - resultOffset;
if (algorithm.ToLower() == "sha256")
this.BufferBytes = this.Func(false);
else
this.BufferBytes = this.Func();
if (needCount > this.BlockSize) { //we one (or more) additional passes
Buffer.BlockCopy(this.BufferBytes, 0, result, resultOffset, this.BlockSize);
resultOffset += this.BlockSize;
} else {
Buffer.BlockCopy(this.BufferBytes, 0, result, resultOffset, needCount);
this.BufferStartIndex = needCount;
this.BufferEndIndex = this.BlockSize;
return result;
}
}
return result;
}
private byte[] Func(bool mscrypto = true) {
var hash1Input = new byte[this.Salt.Length + 4];
Buffer.BlockCopy(this.Salt, 0, hash1Input, 0, this.Salt.Length);
Buffer.BlockCopy(GetBytesFromInt(this.BlockIndex), 0, hash1Input, this.Salt.Length, 4);
var hash1 = this.Algorithm.ComputeHash(hash1Input);
byte[] finalHash = hash1;
for (int i = 2; i <= this.IterationCount; i++) {
hash1 = this.Algorithm.ComputeHash(hash1, 0, hash1.Length);
for (int j = 0; j < this.BlockSize; j++) {
finalHash[j] = (byte)(finalHash[j] ^ hash1[j]);
}
if (mscrypto)
Array.Copy(finalHash, hash1, hash1.Length); // "thank you MS!" -@gentilkiwi
// https://github.com/gentilkiwi/mimikatz/blob/110a831ebe7b529c5dd3010f9e7fced0d3e3a46c/modules/kull_m_crypto.c#L207
}
if (this.BlockIndex == uint.MaxValue) { throw new InvalidOperationException("Derived key too long."); }
this.BlockIndex += 1;
return finalHash;
}
private static byte[] GetBytesFromInt(uint i) {
var bytes = BitConverter.GetBytes(i);
if (BitConverter.IsLittleEndian) {
return new byte[] { bytes[3], bytes[2], bytes[1], bytes[0] };
} else {
return bytes;
}
}
}
}