2 // Mono.Security.Protocol.Ntlm.ChallengeResponse
3 // Implements Challenge Response for NTLM v1 and NTLM v2 Session
6 // Sebastien Pouliot <sebastien@ximian.com>
7 // Martin Baulig <martin.baulig@xamarin.com>
9 // (C) 2003 Motus Technologies Inc. (http://www.motus.com)
10 // (C) 2004 Novell (http://www.novell.com)
11 // (C) 2012 Xamarin, Inc. (http://www.xamarin.com)
14 // a. NTLM Authentication Scheme for HTTP, Ronald Tschalär
15 // http://www.innovation.ch/java/ntlm.html
16 // b. The NTLM Authentication Protocol, Copyright © 2003 Eric Glass
17 // http://davenport.sourceforge.net/ntlm.html
21 // Permission is hereby granted, free of charge, to any person obtaining
22 // a copy of this software and associated documentation files (the
23 // "Software"), to deal in the Software without restriction, including
24 // without limitation the rights to use, copy, modify, merge, publish,
25 // distribute, sublicense, and/or sell copies of the Software, and to
26 // permit persons to whom the Software is furnished to do so, subject to
27 // the following conditions:
29 // The above copyright notice and this permission notice shall be
30 // included in all copies or substantial portions of the Software.
32 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
33 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
34 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
35 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
36 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
37 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
38 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44 using System
.Globalization
;
45 using System
.Security
.Cryptography
;
48 using Mono
.Security
.Cryptography
;
50 namespace Mono
.Security
.Protocol
.Ntlm
{
57 static class ChallengeResponse2
{
59 static private byte[] magic
= { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 }
;
61 // This is the pre-encrypted magic value with a null DES key (0xAAD3B435B51404EE)
62 // Ref: http://packetstormsecurity.nl/Crackers/NT/l0phtcrack/l0phtcrack2.5-readme.html
63 static private byte[] nullEncMagic
= { 0xAA, 0xD3, 0xB4, 0x35, 0xB5, 0x14, 0x04, 0xEE }
;
65 static byte[] Compute_LM (string password
, byte[] challenge
)
67 var buffer
= new byte [21];
69 // create Lan Manager password
70 DES des
= DES
.Create ();
71 des
.Mode
= CipherMode
.ECB
;
72 ICryptoTransform ct
= null;
74 // Note: In .NET DES cannot accept a weak key
75 // this can happen for a null password
76 if ((password
== null) || (password
.Length
< 1)) {
77 Buffer
.BlockCopy (nullEncMagic
, 0, buffer
, 0, 8);
79 des
.Key
= PasswordToKey (password
, 0);
80 ct
= des
.CreateEncryptor ();
81 ct
.TransformBlock (magic
, 0, 8, buffer
, 0);
84 // and if a password has less than 8 characters
85 if ((password
== null) || (password
.Length
< 8)) {
86 Buffer
.BlockCopy (nullEncMagic
, 0, buffer
, 8, 8);
88 des
.Key
= PasswordToKey (password
, 7);
89 ct
= des
.CreateEncryptor ();
90 ct
.TransformBlock (magic
, 0, 8, buffer
, 8);
95 return GetResponse (challenge
, buffer
);
98 static byte[] Compute_NTLM_Password (string password
)
100 var buffer
= new byte [21];
102 // create NT password
103 MD4 md4
= MD4
.Create ();
104 byte[] data
= ((password
== null) ? (new byte [0]) : (Encoding
.Unicode
.GetBytes (password
)));
105 byte[] hash
= md4
.ComputeHash (data
);
106 Buffer
.BlockCopy (hash
, 0, buffer
, 0, 16);
109 Array
.Clear (data
, 0, data
.Length
);
110 Array
.Clear (hash
, 0, hash
.Length
);
115 static byte[] Compute_NTLM (string password
, byte[] challenge
)
117 var buffer
= Compute_NTLM_Password (password
);
118 return GetResponse (challenge
, buffer
);
121 static void Compute_NTLMv2_Session (string password
, byte[] challenge
,
122 out byte[] lm
, out byte[] ntlm
)
124 var nonce
= new byte [8];
125 var rng
= RandomNumberGenerator
.Create ();
126 rng
.GetBytes (nonce
);
128 var sessionNonce
= new byte [challenge
.Length
+ 8];
129 challenge
.CopyTo (sessionNonce
, 0);
130 nonce
.CopyTo (sessionNonce
, challenge
.Length
);
133 nonce
.CopyTo (lm
, 0);
135 MD5 md5
= MD5
.Create ();
137 var hash
= md5
.ComputeHash (sessionNonce
);
138 var newChallenge
= new byte [8];
139 Array
.Copy (hash
, newChallenge
, 8);
141 ntlm
= Compute_NTLM (password
, newChallenge
);
144 Array
.Clear (nonce
, 0, nonce
.Length
);
145 Array
.Clear (sessionNonce
, 0, sessionNonce
.Length
);
146 Array
.Clear (newChallenge
, 0, newChallenge
.Length
);
147 Array
.Clear (hash
, 0, hash
.Length
);
150 static byte[] Compute_NTLMv2 (Type2Message type2
, string username
, string password
, string domain
)
152 var ntlm_hash
= Compute_NTLM_Password (password
);
154 var ubytes
= Encoding
.Unicode
.GetBytes (username
.ToUpperInvariant ());
155 var tbytes
= Encoding
.Unicode
.GetBytes (domain
);
157 var bytes
= new byte [ubytes
.Length
+ tbytes
.Length
];
158 ubytes
.CopyTo (bytes
, 0);
159 Array
.Copy (tbytes
, 0, bytes
, ubytes
.Length
, tbytes
.Length
);
161 var md5
= new HMACMD5 (ntlm_hash
);
162 var ntlm_v2_hash
= md5
.ComputeHash (bytes
);
164 Array
.Clear (ntlm_hash
, 0, ntlm_hash
.Length
);
167 var ntlm_v2_md5
= new HMACMD5 (ntlm_v2_hash
);
169 var now
= DateTime
.Now
;
170 var timestamp
= now
.Ticks
- 504911232000000000;
172 var nonce
= new byte [8];
173 var rng
= RandomNumberGenerator
.Create ();
174 rng
.GetBytes (nonce
);
176 byte[] blob
= new byte [28 + type2
.TargetInfo
.Length
];
180 Buffer
.BlockCopy (BitConverterLE
.GetBytes (timestamp
), 0, blob
, 8, 8);
182 Buffer
.BlockCopy (nonce
, 0, blob
, 16, 8);
183 Buffer
.BlockCopy (type2
.TargetInfo
, 0, blob
, 28, type2
.TargetInfo
.Length
);
185 var challenge
= type2
.Nonce
;
187 var hashInput
= new byte [challenge
.Length
+ blob
.Length
];
188 challenge
.CopyTo (hashInput
, 0);
189 blob
.CopyTo (hashInput
, challenge
.Length
);
191 var blobHash
= ntlm_v2_md5
.ComputeHash (hashInput
);
193 var response
= new byte [blob
.Length
+ blobHash
.Length
];
194 blobHash
.CopyTo (response
, 0);
195 blob
.CopyTo (response
, blobHash
.Length
);
197 Array
.Clear (ntlm_v2_hash
, 0, ntlm_v2_hash
.Length
);
198 ntlm_v2_md5
.Clear ();
199 Array
.Clear (nonce
, 0, nonce
.Length
);
200 Array
.Clear (blob
, 0, blob
.Length
);
201 Array
.Clear (hashInput
, 0, hashInput
.Length
);
202 Array
.Clear (blobHash
, 0, blobHash
.Length
);
207 public static void Compute (Type2Message type2
, NtlmAuthLevel level
,
208 string username
, string password
, string domain
,
209 out byte[] lm
, out byte[] ntlm
)
214 case NtlmAuthLevel
.LM_and_NTLM
:
215 lm
= Compute_LM (password
, type2
.Nonce
);
216 ntlm
= Compute_NTLM (password
, type2
.Nonce
);
219 case NtlmAuthLevel
.LM_and_NTLM_and_try_NTLMv2_Session
:
220 if ((type2
.Flags
& NtlmFlags
.NegotiateNtlm2Key
) == 0)
221 goto case NtlmAuthLevel
.LM_and_NTLM
;
222 Compute_NTLMv2_Session (password
, type2
.Nonce
, out lm
, out ntlm
);
225 case NtlmAuthLevel
.NTLM_only
:
226 if ((type2
.Flags
& NtlmFlags
.NegotiateNtlm2Key
) != 0)
227 Compute_NTLMv2_Session (password
, type2
.Nonce
, out lm
, out ntlm
);
229 ntlm
= Compute_NTLM (password
, type2
.Nonce
);
232 case NtlmAuthLevel
.NTLMv2_only
:
233 ntlm
= Compute_NTLMv2 (type2
, username
, password
, domain
);
237 throw new InvalidOperationException ();
241 static byte[] GetResponse (byte[] challenge
, byte[] pwd
)
243 byte[] response
= new byte [24];
244 DES des
= DES
.Create ();
245 des
.Mode
= CipherMode
.ECB
;
246 des
.Key
= PrepareDESKey (pwd
, 0);
247 ICryptoTransform ct
= des
.CreateEncryptor ();
248 ct
.TransformBlock (challenge
, 0, 8, response
, 0);
249 des
.Key
= PrepareDESKey (pwd
, 7);
250 ct
= des
.CreateEncryptor ();
251 ct
.TransformBlock (challenge
, 0, 8, response
, 8);
252 des
.Key
= PrepareDESKey (pwd
, 14);
253 ct
= des
.CreateEncryptor ();
254 ct
.TransformBlock (challenge
, 0, 8, response
, 16);
258 static byte[] PrepareDESKey (byte[] key56bits
, int position
)
260 // convert to 8 bytes
261 byte[] key
= new byte [8];
262 key
[0] = key56bits
[position
];
263 key
[1] = (byte) ((key56bits
[position
] << 7) | (key56bits
[position
+ 1] >> 1));
264 key
[2] = (byte) ((key56bits
[position
+ 1] << 6) | (key56bits
[position
+ 2] >> 2));
265 key
[3] = (byte) ((key56bits
[position
+ 2] << 5) | (key56bits
[position
+ 3] >> 3));
266 key
[4] = (byte) ((key56bits
[position
+ 3] << 4) | (key56bits
[position
+ 4] >> 4));
267 key
[5] = (byte) ((key56bits
[position
+ 4] << 3) | (key56bits
[position
+ 5] >> 5));
268 key
[6] = (byte) ((key56bits
[position
+ 5] << 2) | (key56bits
[position
+ 6] >> 6));
269 key
[7] = (byte) (key56bits
[position
+ 6] << 1);
273 static byte[] PasswordToKey (string password
, int position
)
275 byte[] key7
= new byte [7];
276 int len
= System
.Math
.Min (password
.Length
- position
, 7);
277 Encoding
.ASCII
.GetBytes (password
.ToUpper (CultureInfo
.CurrentCulture
), position
, len
, key7
, 0);
278 byte[] key8
= PrepareDESKey (key7
, 0);
279 // cleanup intermediate key material
280 Array
.Clear (key7
, 0, key7
.Length
);