-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathIdNTLMOpenSSL.pas
171 lines (141 loc) · 4.98 KB
/
IdNTLMOpenSSL.pas
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
{
This file is part of the Indy (Internet Direct) project, and is offered
under the dual-licensing agreement described on the Indy website.
(http://www.indyproject.org/)
Copyright:
(c) 1993-2024, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
}
unit IdNTLMOpenSSL;
interface
implementation
uses
IdGlobal, IdFIPS, IdSSLOpenSSLHeaders, IdHashMessageDigest,
SysUtils;
{$I IdCompilerDefines.inc}
function LoadOpenSSL: Boolean;
begin
Result := IdSSLOpenSSLHeaders.Load;
end;
function IsNTLMFuncsAvail: Boolean;
begin
Result := Assigned(DES_set_odd_parity) and
Assigned(DES_set_key) and
Assigned(DES_ecb_encrypt);
end;
type
Pdes_key_schedule = ^des_key_schedule;
{/*
* turns a 56 bit key into the 64 bit, odd parity key and sets the key.
* The key schedule ks is also set.
*/}
procedure setup_des_key(key_56: des_cblock; Var ks: des_key_schedule);
Var
key: des_cblock;
begin
key[0] := key_56[0];
key[1] := ((key_56[0] SHL 7) and $FF) or (key_56[1] SHR 1);
key[2] := ((key_56[1] SHL 6) and $FF) or (key_56[2] SHR 2);
key[3] := ((key_56[2] SHL 5) and $FF) or (key_56[3] SHR 3);
key[4] := ((key_56[3] SHL 4) and $FF) or (key_56[4] SHR 4);
key[5] := ((key_56[4] SHL 3) and $FF) or (key_56[5] SHR 5);
key[6] := ((key_56[5] SHL 2) and $FF) or (key_56[6] SHR 6);
key[7] := (key_56[6] SHL 1) and $FF;
DES_set_odd_parity(@key);
DES_set_key(@key, ks);
end;
{/*
* takes a 21 byte array and treats it as 3 56-bit DES keys. The
* 8 byte plaintext is encrypted with each key and the resulting 24
* bytes are stored in the results array.
*/}
procedure calc_resp(keys: PDES_cblock; const ANonce: TIdBytes; results: Pdes_key_schedule);
Var
ks: des_key_schedule;
nonce: des_cblock;
begin
setup_des_key(keys^, ks);
Move(ANonce[0], nonce, 8);
des_ecb_encrypt(@nonce, Pconst_DES_cblock(results), ks, DES_ENCRYPT);
setup_des_key(PDES_cblock(PtrUInt(keys) + 7)^, ks);
des_ecb_encrypt(@nonce, Pconst_DES_cblock(PtrUInt(results) + 8), ks, DES_ENCRYPT);
setup_des_key(PDES_cblock(PtrUInt(keys) + 14)^, ks);
des_ecb_encrypt(@nonce, Pconst_DES_cblock(PtrUInt(results) + 16), ks, DES_ENCRYPT);
end;
Const
Magic: des_cblock = ($4B, $47, $53, $21, $40, $23, $24, $25 );
//* setup LanManager password */
function SetupLanManagerPassword(const APassword: String; const ANonce: TIdBytes): TIdBytes;
var
lm_hpw: array[0..20] of Byte;
lm_pw: array[0..13] of Byte;
idx, len: Integer;
ks: des_key_schedule;
lm_resp: array [0..23] of Byte;
lPassword: {$IFDEF STRING_IS_UNICODE}TIdBytes{$ELSE}AnsiString{$ENDIF};
begin
{$IFDEF STRING_IS_UNICODE}
lPassword := IndyTextEncoding_OSDefault.GetBytes(UpperCase(APassword));
{$ELSE}
lPassword := UpperCase(APassword);
{$ENDIF}
len := IndyMin(Length(lPassword), 14);
if len > 0 then begin
Move(lPassword[{$IFDEF STRING_IS_UNICODE}0{$ELSE}1{$ENDIF}], lm_pw[0], len);
end;
if len < 14 then begin
for idx := len to 13 do begin
lm_pw[idx] := $0;
end;
end;
//* create LanManager hashed password */
setup_des_key(pdes_cblock(@lm_pw[0])^, ks);
des_ecb_encrypt(@magic, Pconst_DES_cblock(@lm_hpw[0]), ks, DES_ENCRYPT);
setup_des_key(pdes_cblock(PtrUInt(@lm_pw[0]) + 7)^, ks);
des_ecb_encrypt(@magic, Pconst_DES_cblock(PtrUInt(@lm_hpw[0]) + 8), ks, DES_ENCRYPT);
FillChar(lm_hpw[16], 5, 0);
calc_resp(PDes_cblock(@lm_hpw[0]), ANonce, Pdes_key_schedule(@lm_resp[0]));
SetLength(Result, SizeOf(lm_resp));
Move(lm_resp[0], Result[0], SizeOf(lm_resp));
end;
//* create NT hashed password */
function CreateNTPassword(const APassword: String; const ANonce: TIdBytes): TIdBytes;
var
nt_hpw: array [1..21] of Byte;
nt_hpw128: TIdBytes;
nt_resp: array [1..24] of Byte;
LMD4: TIdHashMessageDigest4;
{$IFNDEF STRING_IS_UNICODE}
i: integer;
lPwUnicode: TIdBytes;
{$ENDIF}
begin
CheckMD4Permitted;
LMD4 := TIdHashMessageDigest4.Create;
try
{$IFDEF STRING_IS_UNICODE}
nt_hpw128 := LMD4.HashString(APassword, IndyTextEncoding_UTF16LE);
{$ELSE}
// RLebeau: TODO - should this use UTF-16 as well? This logic will
// not produce a valid Unicode string if non-ASCII characters are present!
SetLength(lPwUnicode, Length(S) * SizeOf(WideChar));
for i := 0 to Length(S)-1 do begin
lPwUnicode[i*2] := Byte(S[i+1]);
lPwUnicode[(i*2)+1] := Byte(#0);
end;
nt_hpw128 := LMD4.HashBytes(lPwUnicode);
{$ENDIF}
finally
LMD4.Free;
end;
Move(nt_hpw128[0], nt_hpw[1], 16);
FillChar(nt_hpw[17], 5, 0);
calc_resp(pdes_cblock(@nt_hpw[1]), ANonce, Pdes_key_schedule(@nt_resp[1]));
SetLength(Result, SizeOf(nt_resp));
Move(nt_resp[1], Result[0], SizeOf(nt_resp));
end;
initialization
IdFIPS.LoadNTLMLibrary := LoadOpenSSL;
IdFIPS.IsNTLMFuncsAvail := IsNTLMFuncsAvail;
IdFIPS.NTLMGetLmChallengeResponse := SetupLanManagerPassword;
IdFIPS.NTLMGetNtChallengeResponse := CreateNTPassword;
end.