Shared.EncryptionFunc.pas 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990
  1. unit Shared.EncryptionFunc;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. Encryption functions used by ISCmplr, Setup, and SetupLdr
  8. }
  9. interface
  10. uses
  11. ChaCha20, Shared.Struct;
  12. type
  13. TSpecialCryptContextType = (sccPasswordTest, sccCompressedBlocks1, sccCompressedBlocks2);
  14. procedure GenerateEncryptionKey(const Password: String; const Salt: TSetupKDFSalt;
  15. const Iterations: Integer; out Key: TSetupEncryptionKey);
  16. procedure InitCryptContext(const CryptKey: TSetupEncryptionKey;
  17. const EncryptionBaseNonce: TSetupEncryptionNonce; const StartOffset: Int64; const FirstSlice: Int32;
  18. out CryptContext: TChaCha20Context); overload;
  19. procedure InitCryptContext(const CryptKey: TSetupEncryptionKey;
  20. const EncryptionBaseNonce: TSetupEncryptionNonce; const Typ: TSpecialCryptContextType;
  21. out CryptContext: TChaCha20Context); overload;
  22. procedure GeneratePasswordTest(const CryptKey: TSetupEncryptionKey;
  23. const EncryptionBaseNonce: TSetupEncryptionNonce; out PasswordTest: Integer);
  24. function TestPassword(const CryptKey: TSetupEncryptionKey;
  25. const EncryptionBaseNonce: TSetupEncryptionNonce; const ExpectedPasswordTest: Integer): Boolean;
  26. implementation
  27. uses
  28. SysUtils, PBKDF2;
  29. procedure GenerateEncryptionKey(const Password: String; const Salt: TSetupKDFSalt;
  30. const Iterations: Integer; out Key: TSetupEncryptionKey);
  31. begin
  32. var SaltBytes: TBytes;
  33. var SaltSize := SizeOf(Salt);
  34. SetLength(SaltBytes, SaltSize);
  35. Move(Salt[0], SaltBytes[0], SaltSize);
  36. var KeyLength := SizeOf(Key);
  37. var KeyBytes := PBKDF2SHA256(Password, SaltBytes, Iterations, KeyLength);
  38. Move(KeyBytes[0], Key[0], KeyLength);
  39. end;
  40. procedure InitCryptContext(const CryptKey: TSetupEncryptionKey;
  41. const EncryptionBaseNonce: TSetupEncryptionNonce; const StartOffset: Int64; const FirstSlice: Int32;
  42. out CryptContext: TChaCha20Context);
  43. begin
  44. { Create the unique nonce from the base nonce }
  45. var Nonce := EncryptionBaseNonce;
  46. Nonce.RandomXorStartOffset := Nonce.RandomXorStartOffset xor StartOffset;
  47. Nonce.RandomXorFirstSlice := Nonce.RandomXorFirstSlice xor FirstSlice;
  48. XChaCha20Init(CryptContext, CryptKey[0], Length(CryptKey), Nonce, SizeOf(Nonce), 0);
  49. end;
  50. procedure InitCryptContext(const CryptKey: TSetupEncryptionKey;
  51. const EncryptionBaseNonce: TSetupEncryptionNonce; const Typ: TSpecialCryptContextType;
  52. out CryptContext: TChaCha20Context); overload;
  53. begin
  54. const SpecialFirstSlice = -1-(Ord(Typ)-Ord(Low(Typ)));
  55. InitCryptContext(CryptKey, EncryptionBaseNonce, 0, SpecialFirstSlice, CryptContext);
  56. end;
  57. { This function assumes CryptKey is based on the password }
  58. procedure GeneratePasswordTest(const CryptKey: TSetupEncryptionKey;
  59. const EncryptionBaseNonce: TSetupEncryptionNonce; out PasswordTest: Integer);
  60. begin
  61. var Context: TChaCha20Context;
  62. InitCryptContext(CryptKey, EncryptionBaseNonce, sccPasswordTest, Context);
  63. { Encrypt a value of 0 so Setup can do same and compare the results to test the password }
  64. PasswordTest := 0;
  65. XChaCha20Crypt(Context, PasswordTest, PasswordTest, SizeOf(PasswordTest));
  66. end;
  67. function TestPassword(const CryptKey: TSetupEncryptionKey;
  68. const EncryptionBaseNonce: TSetupEncryptionNonce; const ExpectedPasswordTest: Integer): Boolean;
  69. begin
  70. var ActualPasswordTest: Integer;
  71. GeneratePasswordTest(CryptKey, EncryptionBaseNonce, ActualPasswordTest);
  72. Result := ActualPasswordTest = ExpectedPasswordTest;
  73. end;
  74. end.