IdNTLMOpenSSL.pas 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. {
  2. This file is part of the Indy (Internet Direct) project, and is offered
  3. under the dual-licensing agreement described on the Indy website.
  4. (http://www.indyproject.org/)
  5. Copyright:
  6. (c) 1993-2024, Chad Z. Hower and the Indy Pit Crew. All rights reserved.
  7. }
  8. unit IdNTLMOpenSSL;
  9. interface
  10. implementation
  11. uses
  12. IdGlobal, IdFIPS, IdSSLOpenSSLHeaders, IdHashMessageDigest,
  13. SysUtils;
  14. {$I IdCompilerDefines.inc}
  15. function LoadOpenSSL: Boolean;
  16. begin
  17. Result := IdSSLOpenSSLHeaders.Load;
  18. end;
  19. function IsNTLMFuncsAvail: Boolean;
  20. begin
  21. Result := Assigned(DES_set_odd_parity) and
  22. Assigned(DES_set_key) and
  23. Assigned(DES_ecb_encrypt);
  24. end;
  25. type
  26. Pdes_key_schedule = ^des_key_schedule;
  27. {/*
  28. * turns a 56 bit key into the 64 bit, odd parity key and sets the key.
  29. * The key schedule ks is also set.
  30. */}
  31. procedure setup_des_key(key_56: des_cblock; Var ks: des_key_schedule);
  32. Var
  33. key: des_cblock;
  34. begin
  35. key[0] := key_56[0];
  36. key[1] := ((key_56[0] SHL 7) and $FF) or (key_56[1] SHR 1);
  37. key[2] := ((key_56[1] SHL 6) and $FF) or (key_56[2] SHR 2);
  38. key[3] := ((key_56[2] SHL 5) and $FF) or (key_56[3] SHR 3);
  39. key[4] := ((key_56[3] SHL 4) and $FF) or (key_56[4] SHR 4);
  40. key[5] := ((key_56[4] SHL 3) and $FF) or (key_56[5] SHR 5);
  41. key[6] := ((key_56[5] SHL 2) and $FF) or (key_56[6] SHR 6);
  42. key[7] := (key_56[6] SHL 1) and $FF;
  43. DES_set_odd_parity(@key);
  44. DES_set_key(@key, ks);
  45. end;
  46. {/*
  47. * takes a 21 byte array and treats it as 3 56-bit DES keys. The
  48. * 8 byte plaintext is encrypted with each key and the resulting 24
  49. * bytes are stored in the results array.
  50. */}
  51. procedure calc_resp(keys: PDES_cblock; const ANonce: TIdBytes; results: Pdes_key_schedule);
  52. Var
  53. ks: des_key_schedule;
  54. nonce: des_cblock;
  55. begin
  56. setup_des_key(keys^, ks);
  57. Move(ANonce[0], nonce, 8);
  58. des_ecb_encrypt(@nonce, Pconst_DES_cblock(results), ks, DES_ENCRYPT);
  59. setup_des_key(PDES_cblock(PtrUInt(keys) + 7)^, ks);
  60. des_ecb_encrypt(@nonce, Pconst_DES_cblock(PtrUInt(results) + 8), ks, DES_ENCRYPT);
  61. setup_des_key(PDES_cblock(PtrUInt(keys) + 14)^, ks);
  62. des_ecb_encrypt(@nonce, Pconst_DES_cblock(PtrUInt(results) + 16), ks, DES_ENCRYPT);
  63. end;
  64. Const
  65. Magic: des_cblock = ($4B, $47, $53, $21, $40, $23, $24, $25 );
  66. //* setup LanManager password */
  67. function SetupLanManagerPassword(const APassword: String; const ANonce: TIdBytes): TIdBytes;
  68. var
  69. lm_hpw: array[0..20] of Byte;
  70. lm_pw: array[0..13] of Byte;
  71. idx, len: Integer;
  72. ks: des_key_schedule;
  73. lm_resp: array [0..23] of Byte;
  74. lPassword: {$IFDEF STRING_IS_UNICODE}TIdBytes{$ELSE}AnsiString{$ENDIF};
  75. begin
  76. {$IFDEF STRING_IS_UNICODE}
  77. lPassword := IndyTextEncoding_OSDefault.GetBytes(UpperCase(APassword));
  78. {$ELSE}
  79. lPassword := UpperCase(APassword);
  80. {$ENDIF}
  81. len := IndyMin(Length(lPassword), 14);
  82. if len > 0 then begin
  83. Move(lPassword[{$IFDEF STRING_IS_UNICODE}0{$ELSE}1{$ENDIF}], lm_pw[0], len);
  84. end;
  85. if len < 14 then begin
  86. for idx := len to 13 do begin
  87. lm_pw[idx] := $0;
  88. end;
  89. end;
  90. //* create LanManager hashed password */
  91. setup_des_key(pdes_cblock(@lm_pw[0])^, ks);
  92. des_ecb_encrypt(@magic, Pconst_DES_cblock(@lm_hpw[0]), ks, DES_ENCRYPT);
  93. setup_des_key(pdes_cblock(PtrUInt(@lm_pw[0]) + 7)^, ks);
  94. des_ecb_encrypt(@magic, Pconst_DES_cblock(PtrUInt(@lm_hpw[0]) + 8), ks, DES_ENCRYPT);
  95. FillChar(lm_hpw[16], 5, 0);
  96. calc_resp(PDes_cblock(@lm_hpw[0]), ANonce, Pdes_key_schedule(@lm_resp[0]));
  97. SetLength(Result, SizeOf(lm_resp));
  98. Move(lm_resp[0], Result[0], SizeOf(lm_resp));
  99. end;
  100. //* create NT hashed password */
  101. function CreateNTPassword(const APassword: String; const ANonce: TIdBytes): TIdBytes;
  102. var
  103. nt_hpw: array [1..21] of Byte;
  104. nt_hpw128: TIdBytes;
  105. nt_resp: array [1..24] of Byte;
  106. LMD4: TIdHashMessageDigest4;
  107. {$IFNDEF STRING_IS_UNICODE}
  108. i: integer;
  109. lPwUnicode: TIdBytes;
  110. {$ENDIF}
  111. begin
  112. CheckMD4Permitted;
  113. LMD4 := TIdHashMessageDigest4.Create;
  114. try
  115. {$IFDEF STRING_IS_UNICODE}
  116. nt_hpw128 := LMD4.HashString(APassword, IndyTextEncoding_UTF16LE);
  117. {$ELSE}
  118. // RLebeau: TODO - should this use UTF-16 as well? This logic will
  119. // not produce a valid Unicode string if non-ASCII characters are present!
  120. SetLength(lPwUnicode, Length(S) * SizeOf(WideChar));
  121. for i := 0 to Length(S)-1 do begin
  122. lPwUnicode[i*2] := Byte(S[i+1]);
  123. lPwUnicode[(i*2)+1] := Byte(#0);
  124. end;
  125. nt_hpw128 := LMD4.HashBytes(lPwUnicode);
  126. {$ENDIF}
  127. finally
  128. LMD4.Free;
  129. end;
  130. Move(nt_hpw128[0], nt_hpw[1], 16);
  131. FillChar(nt_hpw[17], 5, 0);
  132. calc_resp(pdes_cblock(@nt_hpw[1]), ANonce, Pdes_key_schedule(@nt_resp[1]));
  133. SetLength(Result, SizeOf(nt_resp));
  134. Move(nt_resp[1], Result[0], SizeOf(nt_resp));
  135. end;
  136. initialization
  137. IdFIPS.LoadNTLMLibrary := LoadOpenSSL;
  138. IdFIPS.IsNTLMFuncsAvail := IsNTLMFuncsAvail;
  139. IdFIPS.NTLMGetLmChallengeResponse := SetupLanManagerPassword;
  140. IdFIPS.NTLMGetNtChallengeResponse := CreateNTPassword;
  141. end.