| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171 |
- {
- 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.
|