Welcome to mirror list, hosted at ThFree Co, Russian Federation.

ChallengeResponse.cs « Mono.Security.Protocol.Ntlm « Mono.Security « class « mcs - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 8e49d8d2eb0c0b7522e610d5c80e9f7758269135 (plain)
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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
//
// Mono.Security.Protocol.Ntlm.ChallengeResponse
//	Implements Challenge Response for NTLM v1
//
// Author:
//	Sebastien Pouliot <sebastien@ximian.com>
//
// (C) 2003 Motus Technologies Inc. (http://www.motus.com)
// (C) 2004 Novell (http://www.novell.com)
//
// References
// a.	NTLM Authentication Scheme for HTTP, Ronald Tschalär
//	http://www.innovation.ch/java/ntlm.html
// b.	The NTLM Authentication Protocol, Copyright © 2003 Eric Glass
//	http://davenport.sourceforge.net/ntlm.html
//

using System;
using System.Globalization;
using System.Security.Cryptography;
using System.Text;

using Mono.Security.Cryptography;

namespace Mono.Security.Protocol.Ntlm {

	public class ChallengeResponse : IDisposable {

		static private byte[] magic = { 0x4B, 0x47, 0x53, 0x21, 0x40, 0x23, 0x24, 0x25 };

		// This is the pre-encrypted magic value with a null DES key (0xAAD3B435B51404EE)
		// Ref: http://packetstormsecurity.nl/Crackers/NT/l0phtcrack/l0phtcrack2.5-readme.html
		static private byte[] nullEncMagic = { 0xAA, 0xD3, 0xB4, 0x35, 0xB5, 0x14, 0x04, 0xEE };

		private bool _disposed;
		private byte[] _challenge;
		private byte[] _lmpwd;
		private byte[] _ntpwd;

		// constructors

		public ChallengeResponse () 
		{
			_disposed = false;
			_lmpwd = new byte [21];
			_ntpwd = new byte [21];
		}
		
		public ChallengeResponse (string password, byte[] challenge) : this ()
		{
			Password = password;
			Challenge = challenge;
		}

		~ChallengeResponse () 
		{
			if (!_disposed)
				Dispose ();
		}

		// properties

		public string Password {
			get { return null; }
			set { 
				if (_disposed)
					throw new ObjectDisposedException ("too late");

				// create Lan Manager password
				DES des = DES.Create ();
				des.Mode = CipherMode.ECB;
				ICryptoTransform ct = null;
				
				// Note: In .NET DES cannot accept a weak key
				// this can happen for a null password
				if ((value == null) || (value.Length < 1)) {
					Buffer.BlockCopy (nullEncMagic, 0, _lmpwd, 0, 8);
				}
				else {
					des.Key = PasswordToKey (value, 0);
					ct = des.CreateEncryptor ();
					ct.TransformBlock (magic, 0, 8, _lmpwd, 0);
				}

				// and if a password has less than 8 characters
				if ((value == null) || (value.Length < 8)) {
					Buffer.BlockCopy (nullEncMagic, 0, _lmpwd, 8, 8);
				}
				else {
					des.Key = PasswordToKey (value, 7);
					ct = des.CreateEncryptor ();
					ct.TransformBlock (magic, 0, 8, _lmpwd, 8);
				}

				// create NT password
				MD4 md4 = MD4.Create ();
				byte[] data = ((value == null) ? (new byte [0]) : (Encoding.Unicode.GetBytes (value)));
				byte[] hash = md4.ComputeHash (data);
				Buffer.BlockCopy (hash, 0, _ntpwd, 0, 16);

				// clean up
				Array.Clear (data, 0, data.Length);
				Array.Clear (hash, 0, hash.Length);
				des.Clear ();
			}
		}

		public byte[] Challenge {
			get { return null; }
			set {
				if (value == null)
					throw new ArgumentNullException ("Challenge");
				if (_disposed)
					throw new ObjectDisposedException ("too late");
				// we don't want the caller to modify the value afterward
				_challenge = (byte[]) value.Clone ();
			}
		}

		public byte[] LM {
			get { 
				if (_disposed)
					throw new ObjectDisposedException ("too late");

				return GetResponse (_lmpwd);
			}
		}

		public byte[] NT {
			get { 
				if (_disposed)
					throw new ObjectDisposedException ("too late");

				return GetResponse (_ntpwd);
			}
		}

		// IDisposable method

		public void Dispose () 
		{
			Dispose (true);
			GC.SuppressFinalize (this);
		}

		private void Dispose (bool disposing) 
		{
			if (!_disposed) {
				// cleanup our stuff
				Array.Clear (_lmpwd, 0, _lmpwd.Length);
				Array.Clear (_ntpwd, 0, _ntpwd.Length);
				if (_challenge != null)
					Array.Clear (_challenge, 0, _challenge.Length);
				_disposed = true;
			}
		}

		// private methods

		private byte[] GetResponse (byte[] pwd) 
		{
			byte[] response = new byte [24];
			DES des = DES.Create ();
			des.Mode = CipherMode.ECB;
			des.Key = PrepareDESKey (pwd, 0);
			ICryptoTransform ct = des.CreateEncryptor ();
			ct.TransformBlock (_challenge, 0, 8, response, 0);
			des.Key = PrepareDESKey (pwd, 7);
			ct = des.CreateEncryptor ();
			ct.TransformBlock (_challenge, 0, 8, response, 8);
			des.Key = PrepareDESKey (pwd, 14);
			ct = des.CreateEncryptor ();
			ct.TransformBlock (_challenge, 0, 8, response, 16);
			return response;
		}

		private byte[] PrepareDESKey (byte[] key56bits, int position) 
		{
			// convert to 8 bytes
			byte[] key = new byte [8];
			key [0] = key56bits [position];
			key [1] = (byte) ((key56bits [position] << 7)     | (key56bits [position + 1] >> 1));
			key [2] = (byte) ((key56bits [position + 1] << 6) | (key56bits [position + 2] >> 2));
			key [3] = (byte) ((key56bits [position + 2] << 5) | (key56bits [position + 3] >> 3));
			key [4] = (byte) ((key56bits [position + 3] << 4) | (key56bits [position + 4] >> 4));
			key [5] = (byte) ((key56bits [position + 4] << 3) | (key56bits [position + 5] >> 5));
			key [6] = (byte) ((key56bits [position + 5] << 2) | (key56bits [position + 6] >> 6));
			key [7] = (byte)  (key56bits [position + 6] << 1);
			return key;
		}

		private byte[] PasswordToKey (string password, int position) 
		{
			byte[] key7 = new byte [7];
			int len = System.Math.Min (password.Length - position, 7);
			Encoding.ASCII.GetBytes (password.ToUpper (CultureInfo.CurrentCulture), position, len, key7, 0);
			byte[] key8 = PrepareDESKey (key7, 0);
			// cleanup intermediate key material
			Array.Clear (key7, 0, key7.Length);
			return key8;
		}
	}
}