Martijn Laan 1 год назад
Родитель
Сommit
abcf3eb742

+ 248 - 0
Components/ChaCha20.pas

@@ -0,0 +1,248 @@
+unit ChaCha20;
+
+{
+  Inno Setup
+  Copyright (C) 1997-2024 Jordan Russell
+  Portions by Martijn Laan
+  For conditions of distribution and use, see LICENSE.TXT.
+
+  ChaCha20 and XChaCha20 encryption/decryption
+
+  Initially based on https://github.com/Ginurx/chacha20-c/tree/master
+}
+
+interface
+
+type
+  TChaCha20Ctx = array[0..15] of Cardinal;
+
+  TChaCha20Context = record
+    ctx, keystream: TChaCha20Ctx;
+    position: 0..64;
+    count64: Boolean;
+  end;
+
+procedure ChaCha20Init(var Context: TChaCha20Context; const Key;
+ const KeyLength: Cardinal; const Nonce; const NonceLength: Cardinal;
+ const Count: Cardinal);
+procedure ChaCha20Crypt(var Context: TChaCha20Context; const InBuffer;
+  var OutBuffer; const Length: Cardinal);
+
+procedure XChaCha20Init(var Context: TChaCha20Context; const Key;
+ const KeyLength: Cardinal; const Nonce; const NonceLength: Cardinal;
+ const Count: Cardinal);
+procedure XChaCha20Crypt(var Context: TChaCha20Context; const InBuffer;
+  var OutBuffer; const Length: Cardinal);
+
+implementation
+
+uses
+  SysUtils;
+
+{$C+}
+
+procedure ChaCha20InitCtx(var ctx: TChaCha20Ctx; const Key;
+  const KeyLength: Cardinal; const Nonce; const NonceLength: Cardinal;
+  const Count: Cardinal);
+begin
+  Assert(KeyLength = 32);
+  Assert(NonceLength in [0, 8, 12]);
+  {$IFDEF DEBUG}
+  FillChar(ctx[0], SizeOf(ctx), 1);
+  {$ENDIF}
+  ctx[0] := $61707865;
+  ctx[1] := $3320646e;
+  ctx[2] := $79622d32;
+  ctx[3] := $6b206574;
+  Move(Key, ctx[4], KeyLength);
+  ctx[12] := Count;
+  if NonceLength = 12 then
+    Move(Nonce, ctx[13], 12)
+  else if NonceLength = 8 then begin
+    ctx[13] := 0;
+    Move(Nonce, ctx[14], 8)
+  end else
+    FillChar(ctx[13], 12, 0);
+end;
+
+procedure ChaCha20Init(var Context: TChaCha20Context; const Key;
+  const KeyLength: Cardinal; const Nonce; const NonceLength: Cardinal;
+  const Count: Cardinal);
+begin
+  ChaCha20InitCtx(Context.ctx, Key, KeyLength, Nonce, NonceLength, Count);
+  Context.position := 64;
+  Context.count64 := NonceLength <> 12;
+end;
+
+procedure ChaCha20RunRounds(var ctx, keystream: TChaCha20Ctx);
+
+  function ROTL(const x: Cardinal; const n: Byte): Cardinal;
+  begin
+    Result := (x shl n) or (x shr (32 - n));
+  end;
+
+  procedure CHACHA20_QR(var a, b, c, d: Cardinal);
+  begin
+    Inc(a, b); d := d xor a; d := ROTL(d, 16);
+    Inc(c, d); b := b xor c; b := ROTL(b, 12);
+    Inc(a, b); d := d xor a; d := ROTL(d, 8);
+    Inc(c, d); b := b xor c; b := ROTL(b, 7);
+  end;
+
+begin
+  Move(ctx, keystream, SizeOf(ctx));
+
+  for var i := 0 to 9 do begin
+    CHACHA20_QR(keystream[0], keystream[4], keystream[8], keystream[12]);  // column 0
+    CHACHA20_QR(keystream[1], keystream[5], keystream[9], keystream[13]);  // column 1
+    CHACHA20_QR(keystream[2], keystream[6], keystream[10], keystream[14]); // column 2
+    CHACHA20_QR(keystream[3], keystream[7], keystream[11], keystream[15]); // column 3
+    CHACHA20_QR(keystream[0], keystream[5], keystream[10], keystream[15]); // diagonal 1 (main diagonal)
+    CHACHA20_QR(keystream[1], keystream[6], keystream[11], keystream[12]); // diagonal 2
+    CHACHA20_QR(keystream[2], keystream[7], keystream[8], keystream[13]);  // diagonal 3
+    CHACHA20_QR(keystream[3], keystream[4], keystream[9], keystream[14]);  // diagonal 4
+  end;
+end;
+
+procedure ChaCha20Crypt(var Context: TChaCha20Context; const InBuffer;
+  var OutBuffer; const Length: Cardinal);
+
+  procedure ChaCha20BlockNext(var ctx, keystream: TChaCha20Ctx; const count64: Boolean);
+  begin
+    ChaCha20RunRounds(ctx, keystream);
+
+    for var i := 0 to 15 do
+      keystream[i] := keystream[i] + ctx[i];
+
+    if count64 then begin
+      if ctx[12] < High(Cardinal) then
+        ctx[12] := ctx[12] + 1
+      else begin
+        ctx[12] := 0;
+        Assert(ctx[13] < High(Cardinal));
+        ctx[13] := ctx[13] + 1;
+      end;
+    end else begin
+      Assert(ctx[12] < High(Cardinal));
+      ctx[12] := ctx[12] + 1;
+    end;
+  end;
+
+begin
+  if Length = 0 then
+    Exit;
+
+  var InBuf: PByte := @InBuffer;
+  var OutBuf: PByte := @OutBuffer;
+  var KeyStream: PByte := @Context.keystream;
+
+  for var I := 0 to Length-1 do begin
+    if Context.position >= 64 then begin
+      ChaCha20BlockNext(Context.ctx, Context.keystream, Context.count64);
+      Context.position := 0;
+    end;
+    OutBuf[I] := InBuf[I] xor KeyStream[Context.position];
+    Inc(Context.position);
+  end;
+end;
+
+procedure HChaCha20(const Key; const KeyLength: Cardinal; const Nonce;
+  const NonceLength: Cardinal; out SubKey: TBytes);
+begin
+  Assert(NonceLength = 16);
+  var NonceBytes: PByte := @Nonce;
+  var ctx: TChaCha20Ctx;
+  ChaCha20InitCtx(ctx, Key, KeyLength, NonceBytes[4], 12, PCardinal(NonceBytes)^);
+  var keystream: TChaCha20Ctx;
+  ChaCha20RunRounds(ctx, keystream);
+  SetLength(SubKey, 32);
+  Move(keystream[0], SubKey[0], 16);
+  Move(keystream[12], SubKey[16], 16);
+end;
+
+procedure XChaCha20Init(var Context: TChaCha20Context; const Key;
+ const KeyLength: Cardinal; const Nonce; const NonceLength: Cardinal;
+ const Count: Cardinal);
+begin
+  Assert(NonceLength = 24);
+  var SubKey: TBytes;
+  HChaCha20(Key, KeyLength, Nonce, 16, SubKey);
+  var NonceBytes: PByte := @Nonce;
+  ChaCha20Init(Context, SubKey[0], Length(SubKey), NonceBytes[16], 8, Count);
+end;
+
+procedure XChaCha20Crypt(var Context: TChaCha20Context; const InBuffer;
+  var OutBuffer; const Length: Cardinal);
+begin
+  ChaCha20Crypt(Context, InBuffer, OutBuffer, Length);
+end;
+
+{.$DEFINE TEST}
+
+{$IFDEF TEST}
+
+procedure TestChaCha20;
+begin
+  //https://datatracker.ietf.org/doc/html/rfc7539#section-2.4.2
+  var Buf: AnsiString := 'Ladies and Gentlemen of the class of ''99: If I could offer you only one tip for the future, sunscreen would be it.';
+  var BufSize := Length(Buf)*SizeOf(Buf[1]);
+  var Key: TBytes := [$00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0a, $0b, $0c, $0d, $0e, $0f, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $1a, $1b, $1c, $1d, $1e, $1f];
+  var Nonce: TBytes := [$00, $00, $00, $00, $00, $00, $00, $4a, $00, $00, $00, $00];
+  var Counter := 1;
+  var Ctx: TChaCha20Context;
+
+  ChaCha20Init(Ctx, Key[0], Length(Key), Nonce[0], Length(Nonce), Counter);
+  ChaCha20Crypt(Ctx, Buf[1], Buf[1], 10);
+  ChaCha20Crypt(Ctx, Buf[11], Buf[11], BufSize-10);
+
+  var CipherText: TBytes := [$6e, $2e, $35, $9a, $25, $68, $f9, $80, $41, $ba, $07, $28, $dd, $0d, $69, $81, $e9, $7e, $7a, $ec, $1d, $43, $60, $c2, $0a, $27, $af, $cc, $fd, $9f, $ae, $0b, $f9, $1b, $65, $c5, $52, $47, $33, $ab, $8f, $59, $3d, $ab, $cd, $62, $b3, $57, $16, $39, $d6, $24, $e6, $51, $52, $ab, $8f, $53, $0c, $35, $9f, $08, $61, $d8, $07, $ca, $0d, $bf, $50, $0d, $6a, $61, $56, $a3, $8e, $08, $8a, $22, $b6, $5e, $52, $bc, $51, $4d, $16, $cc, $f8, $06, $81, $8c, $e9, $1a, $b7, $79, $37, $36, $5a, $f9, $0b, $bf, $74, $a3, $5b, $e6, $b4, $0b, $8e, $ed, $f2, $78, $5e, $42, $87, $4d];
+
+  Assert(Length(Buf) = Length(CipherText));
+  for var I := 0 to Length(Buf)-1 do
+    Assert(Byte(Buf[I+1]) = CipherText[I]);
+end;
+
+procedure TestHChaCha20;
+begin
+  //https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03#section-2.2.1
+  var Key: TBytes := [$00, $01, $02, $03, $04, $05, $06, $07, $08, $09, $0a, $0b, $0c, $0d, $0e, $0f, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $1a, $1b, $1c, $1d, $1e, $1f];
+  var Nonce: TBytes := [$00, $00, $00, $09, $00, $00, $00, $4a, $00, $00, $00, $00, $31, $41, $59, $27];
+  var SubKey: TBytes;
+
+  HChaCha20(Key[0], Length(Key), Nonce[0], Length(Nonce), SubKey);
+
+  var ExpectedSubKey: TBytes := [$82, $41, $3b, $42, $27, $b2, $7b, $fe, $d3, $0e, $42, $50, $8a, $87, $7d, $73, $a0, $f9, $e4, $d5, $8a, $74, $a8, $53, $c1, $2e, $c4, $13, $26, $d3, $ec, $dc];
+
+  Assert(Length(SubKey) = Length(ExpectedSubKey));
+  for var I := 0 to Length(SubKey)-1 do
+    Assert(SubKey[I] = ExpectedSubKey[I]);
+end;
+
+procedure TestXChaCha20;
+begin
+  //https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-xchacha-03#appendix-A.2
+  var Buf: AnsiString := 'The dhole (pronounced "dole") is also known as the Asiatic wild dog, red dog, and whistling dog.'+' It is about the size of a German shepherd but looks more like a long-legged fox. This highly elusive and skilled jumper is classified with wolves, coyotes, jackals, and foxes in the taxonomic family Canidae.';
+  var BufSize := Length(Buf)*SizeOf(Buf[1]);
+  var Key: TBytes := [$80, $81, $82, $83, $84, $85, $86, $87, $88, $89, $8a, $8b, $8c, $8d, $8e, $8f, $90, $91, $92, $93, $94, $95, $96, $97, $98, $99, $9a, $9b, $9c, $9d, $9e, $9f];
+  var Nonce: TBytes := [$40, $41, $42, $43, $44, $45, $46, $47, $48, $49, $4a, $4b, $4c, $4d, $4e, $4f, $50, $51, $52, $53, $54, $55, $56, $58];
+  var Counter := 0;
+  var Ctx: TChaCha20Context;
+
+  XChaCha20Init(Ctx, Key[0], Length(Key), Nonce[0], Length(Nonce), Counter);
+  XChaCha20Crypt(Ctx, Buf[1], Buf[1], BufSize);
+
+  var CipherText: TBytes := [$45, $59, $ab, $ba, $4e, $48, $c1, $61, $02, $e8, $bb, $2c, $05, $e6, $94, $7f, $50, $a7, $86, $de, $16, $2f, $9b, $0b, $7e, $59, $2a, $9b, $53, $d0, $d4, $e9, $8d, $8d, $64, $10, $d5, $40, $a1, $a6, $37, $5b, $26, $d8, $0d, $ac, $e4, $fa, $b5, $23, $84, $c7, $31, $ac, $bf, $16, $a5, $92, $3c, $0c, $48, $d3, $57, $5d, $4d, $0d, $2c, $67, $3b, $66, $6f, $aa, $73, $10, $61, $27, $77, $01, $09, $3a, $6b, $f7, $a1, $58, $a8, $86, $42, $92, $a4, $1c, $48, $e3, $a9, $b4, $c0, $da, $ec, $e0, $f8, $d9, $8d, $0d, $7e, $05, $b3, $7a, $30, $7b, $bb, $66, $33, $31, $64, $ec, $9e, $1b, $24, $ea, $0d, $6c, $3f, $fd, $dc, $ec, $4f, $68, $e7, $44, $30, $56, $19, $3a, $03, $c8, $10, $e1, $13, $44, $ca, $06, $d8, $ed, $8a, $2b, $fb, $1e, $8d, $48, $cf, $a6, $bc, $0e, $b4, $e2, $46, $4b, $74, $81, $42, $40, $7c, $9f, $43, $1a, $ee, $76, $99, $60, $e1, $5b, $a8, $b9, $68, $90, $46, $6e, $f2, $45, $75, $99, $85, $23, $85, $c6, $61, $f7, $52, $ce, $20, $f9, $da, $0c, $09, $ab, $6b, $19, $df, $74, $e7, $6a, $95, $96, $74, $46, $f8, $d0, $fd, $41, $5e, $7b, $ee, $2a, $12, $a1, $14, $c2, $0e, $b5, $29, $2a, $e7, $a3, $49, $ae, $57, $78, $20, $d5, $52, $0a, $1f, $3f, $b6, $2a, $17, $ce, $6a, $7e, $68, $fa, $7c, $79, $11, $1d, $88, $60, $92, $0b, $c0, $48, $ef, $43, $fe, $84, $48, $6c, $cb, $87, $c2, $5f, $0a, $e0, $45, $f0, $cc, $e1, $e7, $98, $9a, $9a, $a2, $20, $a2, $8b, $dd, $48, $27, $e7, $51, $a2, $4a, $6d, $5c, $62, $d7, $90, $a6, $63, $93, $b9, $31, $11, $c1, $a5, $5d, $d7, $42, $1a, $10, $18, $49, $74, $c7, $c5];
+
+  Assert(Length(Buf) = Length(CipherText));
+  for var I := 0 to Length(Buf)-1 do
+    Assert(Byte(Buf[I+1]) = CipherText[I]);
+end;
+
+initialization
+  TestChaCha20;
+  TestHChaCha20;
+  TestXChaCha20;
+
+{$ENDIF}
+
+end.

+ 3 - 4
ISHelp/isetup.xml

@@ -5030,7 +5030,7 @@ DiskSliceSize=1457664
 <body>
 <body>
 <p>Specifies a password you want to prompt the user for at the beginning of the installation.</p>
 <p>Specifies a password you want to prompt the user for at the beginning of the installation.</p>
 <p>When using a password, you might consider setting <link topic="setup_encryption">Encryption</link> to <tt>yes</tt> as well, otherwise files will be stored as plain text and it would not be exceedingly difficult for someone to gain access to them through reverse engineering.</p>
 <p>When using a password, you might consider setting <link topic="setup_encryption">Encryption</link> to <tt>yes</tt> as well, otherwise files will be stored as plain text and it would not be exceedingly difficult for someone to gain access to them through reverse engineering.</p>
-<p>The password itself is not stored as clear text; it's stored as a 160-bit SHA-1 hash, salted with a 64-bit random number. (Note: When encryption is enabled, this stored hash is <i>not</i> used for the encryption key; a different hash with a different salt is generated for that.)</p>
+<p>The password itself is not stored as clear text; it's stored as a 160-bit SHA-1 hash, salted with a 64-bit random number. (Note: When encryption is enabled, this stored hash is <i>not</i> used for the encryption key.)</p>
 </body>
 </body>
 </setuptopic>
 </setuptopic>
 
 
@@ -5437,10 +5437,9 @@ DiskSliceSize=1457664
 <setupvalid><link topic="yesnonotes"><tt>yes</tt> or <tt>no</tt></link></setupvalid>
 <setupvalid><link topic="yesnonotes"><tt>yes</tt> or <tt>no</tt></link></setupvalid>
 <setupdefault><tt>no</tt></setupdefault>
 <setupdefault><tt>no</tt></setupdefault>
 <body>
 <body>
-<p>If set to <tt>yes</tt>, files that are compiled into the installation (via [Files] section entries) will be encrypted using ARCFOUR encryption, with a 160-bit key derived from the value of the <link topic="setup_password">Password</link> [Setup] section directive.</p>
-<p>Because of encryption import/export laws in some countries, encryption support is not included in the main Inno Setup installer but downloaded by it instead. See the <extlink href="https://jrsoftware.org/isdl.php">Inno Setup Downloads</extlink> page for more information.</p>
+<p>If set to <tt>yes</tt>, files that are compiled into the installation (via [Files] section entries) will be encrypted using XChaCha20 encryption, with a 256-bit key derived from the value of the <link topic="setup_password">Password</link> [Setup] section directive.</p>
 <p>If encryption is enabled and you call the <link topic="isxfunc_ExtractTemporaryFile">ExtractTemporaryFile</link> function from the [Code] section prior to the user entering the correct password, the function will fail unless the <tt>noencryption</tt> flag is used on the [Files] section entry for the file.</p>
 <p>If encryption is enabled and you call the <link topic="isxfunc_ExtractTemporaryFile">ExtractTemporaryFile</link> function from the [Code] section prior to the user entering the correct password, the function will fail unless the <tt>noencryption</tt> flag is used on the [Files] section entry for the file.</p>
-<p>The key used for encryption is a 160-bit SHA-1 hash of 64-bit random salt plus the value of <link topic="setup_password">Password</link>.</p>
+<p>The key used for encryption is a 256-bit SHA-256 hash of the value of <link topic="setup_password">Password</link> and the nonce used is a 192-bit random base nonce, appending the index of the first file in the chunk for unique encryption nonces.</p>
 </body>
 </body>
 </setuptopic>
 </setuptopic>
 
 

+ 1 - 1
Projects/ISCmplr.dpr

@@ -33,7 +33,7 @@ uses
   Compression.bzlib in 'Src\Compression.bzlib.pas',
   Compression.bzlib in 'Src\Compression.bzlib.pas',
   Compression.LZMACompressor in 'Src\Compression.LZMACompressor.pas',
   Compression.LZMACompressor in 'Src\Compression.LZMACompressor.pas',
   Shared.FileClass in 'Src\Shared.FileClass.pas',
   Shared.FileClass in 'Src\Shared.FileClass.pas',
-  Shared.ArcFour in 'Src\Shared.ArcFour.pas',
+  ChaCha20 in '..\Components\ChaCha20.pas',
   Shared.VerInfoFunc in 'Src\Shared.VerInfoFunc.pas',
   Shared.VerInfoFunc in 'Src\Shared.VerInfoFunc.pas',
   PathFunc in '..\Components\PathFunc.pas',
   PathFunc in '..\Components\PathFunc.pas',
   Shared.CommonFunc in 'Src\Shared.CommonFunc.pas',
   Shared.CommonFunc in 'Src\Shared.CommonFunc.pas',

+ 1 - 1
Projects/ISCmplr.dproj

@@ -99,7 +99,7 @@
         <DCCReference Include="Src\Compression.bzlib.pas"/>
         <DCCReference Include="Src\Compression.bzlib.pas"/>
         <DCCReference Include="Src\Compression.LZMACompressor.pas"/>
         <DCCReference Include="Src\Compression.LZMACompressor.pas"/>
         <DCCReference Include="Src\Shared.FileClass.pas"/>
         <DCCReference Include="Src\Shared.FileClass.pas"/>
-        <DCCReference Include="Src\Shared.ArcFour.pas"/>
+        <DCCReference Include="..\Components\ChaCha20.pas"/>
         <DCCReference Include="Src\Shared.VerInfoFunc.pas"/>
         <DCCReference Include="Src\Shared.VerInfoFunc.pas"/>
         <DCCReference Include="..\Components\PathFunc.pas"/>
         <DCCReference Include="..\Components\PathFunc.pas"/>
         <DCCReference Include="Src\Shared.CommonFunc.pas"/>
         <DCCReference Include="Src\Shared.CommonFunc.pas"/>

+ 1 - 1
Projects/Setup.dpr

@@ -51,7 +51,7 @@ uses
   Setup.LoggingFunc in 'Src\Setup.LoggingFunc.pas',
   Setup.LoggingFunc in 'Src\Setup.LoggingFunc.pas',
   Setup.DebugClient in 'Src\Setup.DebugClient.pas',
   Setup.DebugClient in 'Src\Setup.DebugClient.pas',
   Shared.DebugStruct in 'Src\Shared.DebugStruct.pas',
   Shared.DebugStruct in 'Src\Shared.DebugStruct.pas',
-  Shared.ArcFour in 'Src\Shared.ArcFour.pas',
+  ChaCha20 in '..\Components\ChaCha20.pas',
   Setup.Uninstall in 'Src\Setup.Uninstall.pas',
   Setup.Uninstall in 'Src\Setup.Uninstall.pas',
   Setup.UninstallProgressForm in 'Src\Setup.UninstallProgressForm.pas' {UninstallProgressForm},
   Setup.UninstallProgressForm in 'Src\Setup.UninstallProgressForm.pas' {UninstallProgressForm},
   Setup.UninstallSharedFileForm in 'Src\Setup.UninstallSharedFileForm.pas' {UninstallSharedFileForm},
   Setup.UninstallSharedFileForm in 'Src\Setup.UninstallSharedFileForm.pas' {UninstallSharedFileForm},

+ 1 - 1
Projects/Setup.dproj

@@ -120,7 +120,7 @@
         <DCCReference Include="Src\Setup.LoggingFunc.pas"/>
         <DCCReference Include="Src\Setup.LoggingFunc.pas"/>
         <DCCReference Include="Src\Setup.DebugClient.pas"/>
         <DCCReference Include="Src\Setup.DebugClient.pas"/>
         <DCCReference Include="Src\Shared.DebugStruct.pas"/>
         <DCCReference Include="Src\Shared.DebugStruct.pas"/>
-        <DCCReference Include="Src\Shared.ArcFour.pas"/>
+        <DCCReference Include="..\Components\ChaCha20.pas"/>
         <DCCReference Include="Src\Setup.Uninstall.pas"/>
         <DCCReference Include="Src\Setup.Uninstall.pas"/>
         <DCCReference Include="Src\Setup.UninstallProgressForm.pas">
         <DCCReference Include="Src\Setup.UninstallProgressForm.pas">
             <Form>UninstallProgressForm</Form>
             <Form>UninstallProgressForm</Form>

+ 13 - 26
Projects/Src/Compiler.CompressionHandler.pas

@@ -12,7 +12,7 @@ unit Compiler.CompressionHandler;
 interface
 interface
 
 
 uses
 uses
-  SHA1, Shared.ArcFour, Shared.Int64Em, Shared.FileClass, Compression.Base,
+  SHA1, ChaCha20, Shared.Int64Em, Shared.FileClass, Compression.Base,
   Compiler.StringLists, Compiler.SetupCompiler;
   Compiler.StringLists, Compiler.SetupCompiler;
 
 
 type
 type
@@ -27,7 +27,7 @@ type
     FChunkFirstSlice: Integer;
     FChunkFirstSlice: Integer;
     FChunkStarted: Boolean;
     FChunkStarted: Boolean;
     FChunkStartOffset: Longint;
     FChunkStartOffset: Longint;
-    FCryptContext: TArcFourContext;
+    FCryptContext: TChaCha20Context;
     FCurSlice: Integer;
     FCurSlice: Integer;
     FDestFile: TFile;
     FDestFile: TFile;
     FDestFileIsDiskSlice: Boolean;
     FDestFileIsDiskSlice: Boolean;
@@ -61,7 +61,7 @@ type
 implementation
 implementation
 
 
 uses
 uses
-  SysUtils, Shared.Struct, Compiler.Messages, Compiler.HelperFunc;
+  SysUtils, Hash, Shared.Struct, Compiler.Messages, Compiler.HelperFunc;
 
 
 constructor TCompressionHandler.Create(ACompiler: TSetupCompiler;
 constructor TCompressionHandler.Create(ACompiler: TSetupCompiler;
   const InitialSliceFilename: String);
   const InitialSliceFilename: String);
@@ -189,27 +189,16 @@ procedure TCompressionHandler.NewChunk(const ACompressorClass: TCustomCompressor
   end;
   end;
 
 
   procedure InitEncryption;
   procedure InitEncryption;
-  var
-    Salt: TSetupSalt;
-    Context: TSHA1Context;
-    Hash: TSHA1Digest;
   begin
   begin
-    { Generate and write a random salt. This salt is hashed into the key to
-      prevent the same key from ever being used twice (theoretically). }
-    GenerateRandomBytes(Salt, SizeOf(Salt));
-    FDestFile.WriteBuffer(Salt, SizeOf(Salt));
-
-    { Create an SHA-1 hash of the salt plus ACryptKey, and use that as the key }
-    SHA1Init(Context);
-    SHA1Update(Context, Salt, SizeOf(Salt));
-    SHA1Update(Context, Pointer(ACryptKey)^, Length(ACryptKey)*SizeOf(ACryptKey[1]));
-    Hash := SHA1Final(Context);
-    ArcFourInit(FCryptContext, Hash, SizeOf(Hash));
-
-    { Discard first 1000 bytes of the output keystream, since according to
-      <http://en.wikipedia.org/wiki/RC4_(cipher)>, "the first few bytes of
-      output keystream are strongly non-random." }
-    ArcFourDiscard(FCryptContext, 1000);
+    { Create an SHA-256 hash of ACryptKey, and use that as the key }
+    var Key := THashSHA2.GetHashBytes(ACryptKey, SHA256);
+
+    { Create a unique nonce from the base nonce }
+    var Nonce := FCompiler.GetEncryptionBaseNonce;
+    Nonce.RandomXorStartOffset := Nonce.RandomXorStartOffset xor FChunkStartOffset;
+    Nonce.RandomXorFirstSlice := Nonce.RandomXorFirstSlice xor FChunkFirstSlice;
+
+    XChaCha20Init(FCryptContext, Key[0], Length(Key), Nonce, SizeOf(Nonce), 0);
   end;
   end;
 
 
 var
 var
@@ -220,8 +209,6 @@ begin
   { If there isn't enough room left to start a new chunk on the current slice,
   { If there isn't enough room left to start a new chunk on the current slice,
     start a new slice }
     start a new slice }
   MinBytesLeft := SizeOf(ZLIBID);
   MinBytesLeft := SizeOf(ZLIBID);
-  if AUseEncryption then
-    Inc(MinBytesLeft, SizeOf(TSetupSalt));
   Inc(MinBytesLeft);  { for at least one byte of data }
   Inc(MinBytesLeft);  { for at least one byte of data }
   if FSliceBytesLeft < MinBytesLeft then
   if FSliceBytesLeft < MinBytesLeft then
     NewSlice('');
     NewSlice('');
@@ -311,7 +298,7 @@ begin
         temporary buffer. }
         temporary buffer. }
       GetMem(P2, S);
       GetMem(P2, S);
       try
       try
-        ArcFourCrypt(FCryptContext, P^, P2^, S);
+        XChaCha20Crypt(FCryptContext, P^, P2^, S);
         FDestFile.WriteBuffer(P2^, S)
         FDestFile.WriteBuffer(P2^, S)
       finally
       finally
         FreeMem(P2);
         FreeMem(P2);

+ 0 - 3
Projects/Src/Compiler.Messages.pas

@@ -49,7 +49,6 @@ const
   SCompilerStatusCreateSetupFiles = 'Creating setup files';
   SCompilerStatusCreateSetupFiles = 'Creating setup files';
   SCompilerStatusSkippingCreateSetupFiles = 'Skipping creating setup files, output is disabled';
   SCompilerStatusSkippingCreateSetupFiles = 'Skipping creating setup files, output is disabled';
   SCompilerStatusCreateManifestFile = 'Creating manifest file';
   SCompilerStatusCreateManifestFile = 'Creating manifest file';
-  SCompilerStatusFilesInitEncryption = '   Initializing encryption';
   SCompilerStatusFilesCompressing = '   Compressing: %s';
   SCompilerStatusFilesCompressing = '   Compressing: %s';
   SCompilerStatusFilesCompressingVersion = '   Compressing: %s   (%u.%u.%u.%u)';
   SCompilerStatusFilesCompressingVersion = '   Compressing: %s   (%u.%u.%u.%u)';
   SCompilerStatusFilesStoring = '   Storing: %s';
   SCompilerStatusFilesStoring = '   Storing: %s';
@@ -92,8 +91,6 @@ const
   SCompilerSetup0Mismatch = 'Internal error SC1';
   SCompilerSetup0Mismatch = 'Internal error SC1';
   SCompilerMustUseDiskSpanning = 'Disk spanning must be enabled in order to create an installation larger than %d bytes in size';
   SCompilerMustUseDiskSpanning = 'Disk spanning must be enabled in order to create an installation larger than %d bytes in size';
   SCompilerCompileCodeError = 'An error occurred while trying to compile the [Code] section:' + SNewLine2 + '%s';
   SCompilerCompileCodeError = 'An error occurred while trying to compile the [Code] section:' + SNewLine2 + '%s';
-  SCompilerISCryptMissing = 'Cannot use encryption because ISCrypt.dll is missing.' + SNewLine2 +
-    'Note: This file is not installed with Inno Setup. A link to obtain it can be found on the Inno Setup web site';
   SCompilerFunctionFailedWithCode = '%s failed. Error %d: %s';
   SCompilerFunctionFailedWithCode = '%s failed. Error %d: %s';
 
 
   { [Setup] }
   { [Setup] }

+ 16 - 36
Projects/Src/Compiler.SetupCompiler.pas

@@ -202,7 +202,6 @@ type
     function FindSignToolIndexByName(const AName: String): Integer;
     function FindSignToolIndexByName(const AName: String): Integer;
     function GetLZMAExeFilename(const Allow64Bit: Boolean): String;
     function GetLZMAExeFilename(const Allow64Bit: Boolean): String;
     procedure InitBzipDLL;
     procedure InitBzipDLL;
-    procedure InitCryptDLL;
     procedure InitPreLangData(const APreLangData: TPreLangData);
     procedure InitPreLangData(const APreLangData: TPreLangData);
     procedure InitLanguageEntry(var ALanguageEntry: TSetupLanguageEntry);
     procedure InitLanguageEntry(var ALanguageEntry: TSetupLanguageEntry);
     procedure InitLZMADLL;
     procedure InitLZMADLL;
@@ -272,6 +271,7 @@ type
     function GetDebugInfo: TMemoryStream;
     function GetDebugInfo: TMemoryStream;
     function GetDiskSliceSize:Longint;
     function GetDiskSliceSize:Longint;
     function GetDiskSpanning: Boolean;
     function GetDiskSpanning: Boolean;
+    function GetEncryptionBaseNonce: TSetupNonce;
     function GetExeFilename: String;
     function GetExeFilename: String;
     function GetLineFilename: String;
     function GetLineFilename: String;
     function GetLineNumber: Integer;
     function GetLineNumber: Integer;
@@ -291,7 +291,7 @@ implementation
 uses
 uses
   Commctrl, TypInfo, AnsiStrings, Math, WideStrUtils,
   Commctrl, TypInfo, AnsiStrings, Math, WideStrUtils,
   PathFunc, Shared.CommonFunc, Compiler.Messages, Shared.SetupEntFunc,
   PathFunc, Shared.CommonFunc, Compiler.Messages, Shared.SetupEntFunc,
-  Shared.FileClass, Compression.Base, Compression.Zlib, Compression.bzlib, Shared.ArcFour, SHA1,
+  Shared.FileClass, Compression.Base, Compression.Zlib, Compression.bzlib, SHA1,
   Shared.LangOptionsSectionDirectives, Shared.ResUpdateFunc, Compiler.ExeUpdateFunc,
   Shared.LangOptionsSectionDirectives, Shared.ResUpdateFunc, Compiler.ExeUpdateFunc,
 {$IFDEF STATICPREPROC}
 {$IFDEF STATICPREPROC}
   ISPP.Preprocess,
   ISPP.Preprocess,
@@ -310,7 +310,7 @@ type
   end;
   end;
 
 
 var
 var
-  ZipInitialized, BzipInitialized, LZMAInitialized, CryptInitialized: Boolean;
+  ZipInitialized, BzipInitialized, LZMAInitialized: Boolean;
   PreprocessorInitialized: Boolean;
   PreprocessorInitialized: Boolean;
   PreprocessScriptProc: TPreprocessScriptProc;
   PreprocessScriptProc: TPreprocessScriptProc;
 
 
@@ -597,6 +597,11 @@ begin
   Result := DiskSpanning;
   Result := DiskSpanning;
 end;
 end;
 
 
+function TSetupCompiler.GetEncryptionBaseNonce: TSetupNonce;
+begin
+  Result := SetupHeader.EncryptionBaseNonce;
+end;
+
 function TSetupCompiler.GetExeFilename: String;
 function TSetupCompiler.GetExeFilename: String;
 begin
 begin
   Result := ExeFilename;
   Result := ExeFilename;
@@ -659,20 +664,6 @@ begin
   Result := SlicesPerDisk;
   Result := SlicesPerDisk;
 end;
 end;
 
 
-procedure TSetupCompiler.InitCryptDLL;
-var
-  M: HMODULE;
-begin
-  if CryptInitialized then
-    Exit;
-  M := SafeLoadLibrary(CompilerDir + 'iscrypt.dll', SEM_NOOPENFILEERRORBOX);
-  if M = 0 then
-    AbortCompileFmt('Failed to load iscrypt.dll (%d)', [GetLastError]);
-  if not ArcFourInitFunctions(M) then
-    AbortCompile('Failed to get address of functions in iscrypt.dll');
-  CryptInitialized := True;
-end;
-
 function TSetupCompiler.FilenameToFileIndex(const AFilename: String): Integer;
 function TSetupCompiler.FilenameToFileIndex(const AFilename: String): Integer;
 begin
 begin
   if not GotPrevFilename or (PathCompare(AFilename, PrevFilename) <> 0) then begin
   if not GotPrevFilename or (PathCompare(AFilename, PrevFilename) <> 0) then begin
@@ -2377,6 +2368,11 @@ var
     Hash := SHA1Final(Context);
     Hash := SHA1Final(Context);
   end;
   end;
 
 
+  procedure GenerateEncryptionBaseNonce(var Nonce: TSetupNonce);
+  begin
+    GenerateRandomBytes(Nonce, SizeOf(Nonce));
+  end;
+
   procedure StrToTouchDate(const S: String);
   procedure StrToTouchDate(const S: String);
   var
   var
     P: PChar;
     P: PChar;
@@ -2820,6 +2816,8 @@ begin
     ssEncryption:
     ssEncryption:
       begin
       begin
         SetSetupHeaderOption(shEncryptionUsed);
         SetSetupHeaderOption(shEncryptionUsed);
+        if shEncryptionUsed in SetupHeader.Options then
+          GenerateEncryptionBaseNonce(SetupHeader.EncryptionBaseNonce);
       end;
       end;
     ssExtraDiskSpaceRequired: begin
     ssExtraDiskSpaceRequired: begin
         if not StrToInteger64(Value, SetupHeader.ExtraDiskSpaceRequired) then
         if not StrToInteger64(Value, SetupHeader.ExtraDiskSpaceRequired) then
@@ -6633,7 +6631,7 @@ var
   ExeFile: TFile;
   ExeFile: TFile;
   LicenseText, InfoBeforeText, InfoAfterText: AnsiString;
   LicenseText, InfoBeforeText, InfoAfterText: AnsiString;
   WizardImages, WizardSmallImages: TObjectList<TCustomMemoryStream>;
   WizardImages, WizardSmallImages: TObjectList<TCustomMemoryStream>;
-  DecompressorDLL, DecryptionDLL: TMemoryStream;
+  DecompressorDLL: TMemoryStream;
 
 
   SetupLdrOffsetTable: TSetupLdrOffsetTable;
   SetupLdrOffsetTable: TSetupLdrOffsetTable;
   SizeOfExe, SizeOfHeaders: Longint;
   SizeOfExe, SizeOfHeaders: Longint;
@@ -6739,8 +6737,6 @@ var
         WriteStream(WizardSmallImages[J], W);
         WriteStream(WizardSmallImages[J], W);
       if SetupHeader.CompressMethod in [cmZip, cmBzip] then
       if SetupHeader.CompressMethod in [cmZip, cmBzip] then
         WriteStream(DecompressorDLL, W);
         WriteStream(DecompressorDLL, W);
-      if shEncryptionUsed in SetupHeader.Options then
-        WriteStream(DecryptionDLL, W);
 
 
       W.Finish;
       W.Finish;
     finally
     finally
@@ -6908,12 +6904,6 @@ var
     ChunkCompressed := False;  { avoid warning }
     ChunkCompressed := False;  { avoid warning }
     CH := TCompressionHandler.Create(Self, FirstDestFile);
     CH := TCompressionHandler.Create(Self, FirstDestFile);
     try
     try
-      { If encryption is used, load the encryption DLL }
-      if shEncryptionUsed in SetupHeader.Options then begin
-        AddStatus(SCompilerStatusFilesInitEncryption);
-        InitCryptDLL;
-      end;
-
       if DiskSpanning then begin
       if DiskSpanning then begin
         if not CH.ReserveBytesOnSlice(BytesToReserveOnFirstDisk) then
         if not CH.ReserveBytesOnSlice(BytesToReserveOnFirstDisk) then
           AbortCompile(SCompilerNotEnoughSpaceOnFirstDisk);
           AbortCompile(SCompilerNotEnoughSpaceOnFirstDisk);
@@ -7356,7 +7346,6 @@ begin
   WizardSmallImages := nil;
   WizardSmallImages := nil;
   SetupE32 := nil;
   SetupE32 := nil;
   DecompressorDLL := nil;
   DecompressorDLL := nil;
-  DecryptionDLL := nil;
 
 
   try
   try
     Finalize(SetupHeader);
     Finalize(SetupHeader);
@@ -7870,14 +7859,6 @@ begin
         end;
         end;
     end;
     end;
 
 
-    { Read decryption DLL }
-    if shEncryptionUsed in SetupHeader.Options then begin
-      AddStatus(Format(SCompilerStatusReadingFile, ['iscrypt.dll']));
-      if not NewFileExists(CompilerDir + 'iscrypt.dll') then
-        AbortCompile(SCompilerISCryptMissing);
-      DecryptionDLL := CreateMemoryStreamFromFile(CompilerDir + 'iscrypt.dll');
-    end;
-
     { Add default types if necessary }
     { Add default types if necessary }
     if (ComponentEntries.Count > 0) and (TypeEntries.Count = 0) then begin
     if (ComponentEntries.Count > 0) and (TypeEntries.Count = 0) then begin
       AddDefaultSetupType(DefaultTypeEntryNames[0], [], ttDefaultFull);
       AddDefaultSetupType(DefaultTypeEntryNames[0], [], ttDefaultFull);
@@ -8057,7 +8038,6 @@ begin
     UsedUserAreas.Clear;
     UsedUserAreas.Clear;
     WarningsList.Clear;
     WarningsList.Clear;
     { Free all the data }
     { Free all the data }
-    DecryptionDLL.Free;
     DecompressorDLL.Free;
     DecompressorDLL.Free;
     SetupE32.Free;
     SetupE32.Free;
     WizardSmallImages.Free;
     WizardSmallImages.Free;

+ 0 - 6
Projects/Src/IDE.HelperFunc.pas

@@ -37,7 +37,6 @@ procedure AddFileToRecentDocs(const Filename: String);
 function GenerateGuid: String;
 function GenerateGuid: String;
 function ISPPInstalled: Boolean;
 function ISPPInstalled: Boolean;
 function IsISPPBuiltins(const Filename: String): Boolean;
 function IsISPPBuiltins(const Filename: String): Boolean;
-function ISCryptInstalled: Boolean;
 function WindowsVersionAtLeast(const AMajor, AMinor: Byte; const ABuild: Word = 0): Boolean;
 function WindowsVersionAtLeast(const AMajor, AMinor: Byte; const ABuild: Word = 0): Boolean;
 function IsWindows10: Boolean;
 function IsWindows10: Boolean;
 function IsWindows11: Boolean;
 function IsWindows11: Boolean;
@@ -243,11 +242,6 @@ begin
   Result := PathCompare(PathExtractName(Filename), 'ISPPBuiltins.iss') = 0;
   Result := PathCompare(PathExtractName(Filename), 'ISPPBuiltins.iss') = 0;
 end;
 end;
 
 
-function ISCryptInstalled: Boolean;
-begin
-  Result := NewFileExists(PathExtractPath(NewParamStr(0)) + 'iscrypt.dll');
-end;
-
 var
 var
   WindowsVersion: Cardinal;
   WindowsVersion: Cardinal;
 
 

+ 1 - 2
Projects/Src/IDE.Wizard.WizardForm.pas

@@ -373,7 +373,6 @@ begin
 
 
   { Compiler }
   { Compiler }
   OutputBaseFileNameEdit.Text := 'mysetup';
   OutputBaseFileNameEdit.Text := 'mysetup';
-  EncryptionCheck.Visible := ISCryptInstalled;
   EncryptionCheck.Checked := True;
   EncryptionCheck.Checked := True;
   EncryptionCheck.Enabled := False;
   EncryptionCheck.Enabled := False;
 
 
@@ -1053,7 +1052,7 @@ begin
       Setup := Setup + 'SetupIconFile=' + SetupIconFileEdit.Text + SNewLine;
       Setup := Setup + 'SetupIconFile=' + SetupIconFileEdit.Text + SNewLine;
     if PasswordEdit.Text <> '' then begin
     if PasswordEdit.Text <> '' then begin
       Setup := Setup + 'Password=' + PasswordEdit.Text + SNewLine;
       Setup := Setup + 'Password=' + PasswordEdit.Text + SNewLine;
-      if ISCryptInstalled and EncryptionCheck.Checked then
+      if EncryptionCheck.Checked then
         Setup := Setup + 'Encryption=yes' + SNewLine;
         Setup := Setup + 'Encryption=yes' + SNewLine;
     end;
     end;
 
 

+ 16 - 24
Projects/Src/Setup.FileExtractor.pas

@@ -13,7 +13,7 @@ interface
 
 
 uses
 uses
   Windows, SysUtils, Shared.Int64Em, Shared.FileClass, Compression.Base,
   Windows, SysUtils, Shared.Int64Em, Shared.FileClass, Compression.Base,
-  Shared.Struct, Shared.ArcFour;
+  Shared.Struct, ChaCha20;
 
 
 type
 type
   TExtractorProgressProc = procedure(Bytes: Cardinal);
   TExtractorProgressProc = procedure(Bytes: Cardinal);
@@ -27,7 +27,7 @@ type
     FChunkBytesLeft, FChunkDecompressedBytesRead: Integer64;
     FChunkBytesLeft, FChunkDecompressedBytesRead: Integer64;
     FNeedReset: Boolean;
     FNeedReset: Boolean;
     FChunkCompressed, FChunkEncrypted: Boolean;
     FChunkCompressed, FChunkEncrypted: Boolean;
-    FCryptContext: TArcFourContext;
+    FCryptContext: TChaCha20Context;
     FCryptKey: String;
     FCryptKey: String;
     FEntered: Integer;
     FEntered: Integer;
     procedure DecompressBytes(var Buffer; Count: Cardinal);
     procedure DecompressBytes(var Buffer; Count: Cardinal);
@@ -50,8 +50,8 @@ procedure FreeFileExtractor;
 implementation
 implementation
 
 
 uses
 uses
-  PathFunc, Shared.CommonFunc, Setup.MainFunc, SetupLdrAndSetup.Messages, Shared.SetupMessageIDs,
-  Setup.InstFunc, Compression.Zlib, Compression.bzlib,
+  Hash, PathFunc, Shared.CommonFunc, Setup.MainFunc, SetupLdrAndSetup.Messages,
+  Shared.SetupMessageIDs, Setup.InstFunc, Compression.Zlib, Compression.bzlib,
   Compression.LZMADecompressor, SHA1, Setup.LoggingFunc, Setup.NewDiskForm;
   Compression.LZMADecompressor, SHA1, Setup.LoggingFunc, Setup.NewDiskForm;
 
 
 var
 var
@@ -189,25 +189,16 @@ procedure TFileExtractor.SeekTo(const FL: TSetupFileLocationEntry;
   const ProgressProc: TExtractorProgressProc);
   const ProgressProc: TExtractorProgressProc);
 
 
   procedure InitDecryption;
   procedure InitDecryption;
-  var
-    Salt: TSetupSalt;
-    Context: TSHA1Context;
-    Hash: TSHA1Digest;
   begin
   begin
-    { Read the salt }
-    if FSourceF.Read(Salt, SizeOf(Salt)) <> SizeOf(Salt) then
-      SourceIsCorrupted('Failed to read salt');
+    { Initialize the key, which is the SHA-256 hash of FCryptKey }
+    var Key := THashSHA2.GetHashBytes(FCryptKey, SHA256);
 
 
-    { Initialize the key, which is the SHA-1 hash of the salt plus FCryptKey }
-    SHA1Init(Context);
-    SHA1Update(Context, Salt, SizeOf(Salt));
-    SHA1Update(Context, Pointer(FCryptKey)^, Length(FCryptKey)*SizeOf(FCryptKey[1]));
-    Hash := SHA1Final(Context);
-    ArcFourInit(FCryptContext, Hash, SizeOf(Hash));
-
-    { The compiler discards the first 1000 bytes for extra security,
-      so we must as well }
-    ArcFourDiscard(FCryptContext, 1000);
+    { Recreate the unique nonce from the base nonce }
+    var Nonce := SetupHeader.EncryptionBaseNonce;
+    Nonce.RandomXorStartOffset := Nonce.RandomXorStartOffset xor FChunkStartOffset;
+    Nonce.RandomXorFirstSlice := Nonce.RandomXorFirstSlice xor FChunkFirstSlice;
+
+    XChaCha20Init(FCryptContext, Key[0], Length(Key), Nonce, SizeOf(Nonce), 0);
   end;
   end;
 
 
   procedure Discard(Count: Integer64);
   procedure Discard(Count: Integer64);
@@ -262,8 +253,6 @@ begin
         SourceIsCorrupted('Failed to read CompID');
         SourceIsCorrupted('Failed to read CompID');
       if Longint(TestCompID) <> Longint(ZLIBID) then
       if Longint(TestCompID) <> Longint(ZLIBID) then
         SourceIsCorrupted('Invalid CompID');
         SourceIsCorrupted('Invalid CompID');
-      if foChunkEncrypted in FL.Flags then
-        InitDecryption;
 
 
       FChunkFirstSlice := FL.FirstSlice;
       FChunkFirstSlice := FL.FirstSlice;
       FChunkLastSlice := FL.LastSlice;
       FChunkLastSlice := FL.LastSlice;
@@ -273,6 +262,9 @@ begin
       FChunkDecompressedBytesRead.Lo := 0;
       FChunkDecompressedBytesRead.Lo := 0;
       FChunkCompressed := foChunkCompressed in FL.Flags;
       FChunkCompressed := foChunkCompressed in FL.Flags;
       FChunkEncrypted := foChunkEncrypted in FL.Flags;
       FChunkEncrypted := foChunkEncrypted in FL.Flags;
+
+      if foChunkEncrypted in FL.Flags then
+        InitDecryption;
     end;
     end;
 
 
     { Need to seek forward in the chunk? }
     { Need to seek forward in the chunk? }
@@ -302,7 +294,7 @@ begin
 
 
     { Decrypt the data after reading from the file }
     { Decrypt the data after reading from the file }
     if FChunkEncrypted then
     if FChunkEncrypted then
-      ArcFourCrypt(FCryptContext, Buffer^, Buffer^, Res);
+      ChaCha20Crypt(FCryptContext, Buffer^, Buffer^, Res);
 
 
     if Left = Res then
     if Left = Res then
       Break
       Break

+ 3 - 34
Projects/Src/Setup.MainFunc.pas

@@ -241,7 +241,7 @@ uses
   SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.Install, SetupLdrAndSetup.InstFunc,
   SetupLdrAndSetup.Messages, Shared.SetupMessageIDs, Setup.Install, SetupLdrAndSetup.InstFunc,
   Setup.InstFunc, SetupLdrAndSetup.RedirFunc, PathFunc,
   Setup.InstFunc, SetupLdrAndSetup.RedirFunc, PathFunc,
   Compression.Base, Compression.Zlib, Compression.bzlib, Compression.LZMADecompressor,
   Compression.Base, Compression.Zlib, Compression.bzlib, Compression.LZMADecompressor,
-  Shared.ArcFour, Shared.SetupEntFunc, Setup.SelectLanguageForm,
+  Shared.SetupEntFunc, Setup.SelectLanguageForm,
   Setup.WizardForm, Setup.DebugClient, Shared.VerInfoFunc, Setup.FileExtractor,
   Setup.WizardForm, Setup.DebugClient, Shared.VerInfoFunc, Setup.FileExtractor,
   Shared.FileClass, Setup.LoggingFunc, SHA1, ActiveX,
   Shared.FileClass, Setup.LoggingFunc, SHA1, ActiveX,
   SimpleExpression, Setup.Helper, Setup.SpawnClient, Setup.SpawnServer,
   SimpleExpression, Setup.Helper, Setup.SpawnClient, Setup.SpawnServer,
@@ -257,7 +257,6 @@ var
     var ppszPath: PWideChar): HRESULT; stdcall;
     var ppszPath: PWideChar): HRESULT; stdcall;
 
 
   DecompressorDLLHandle: HMODULE;
   DecompressorDLLHandle: HMODULE;
-  DecryptDLLHandle: HMODULE;
 
 
 type
 type
   TDummyClass = class
   TDummyClass = class
@@ -2543,7 +2542,7 @@ procedure InitializeSetup;
 { Initializes various vars used by the setup. This is called in the project
 { Initializes various vars used by the setup. This is called in the project
   source. }
   source. }
 var
 var
-  DecompressorDLL, DecryptDLL: TMemoryStream;
+  DecompressorDLL: TMemoryStream;
 
 
   function ExtractLongWord(var S: String): LongWord;
   function ExtractLongWord(var S: String): LongWord;
   var
   var
@@ -2635,20 +2634,6 @@ var
     end;
     end;
   end;
   end;
 
 
-  procedure LoadDecryptDLL;
-  var
-    Filename: String;
-  begin
-    Filename := AddBackslash(TempInstallDir) + '_isetup\_iscrypt.dll';
-    SaveStreamToTempFile(DecryptDLL, Filename);
-    FreeAndNil(DecryptDLL);
-    DecryptDLLHandle := SafeLoadLibrary(Filename, SEM_NOOPENFILEERRORBOX);
-    if DecryptDLLHandle = 0 then
-      InternalError(Format('Failed to load DLL "%s"', [Filename]));
-    if not ArcFourInitFunctions(DecryptDLLHandle) then
-      InternalError('ISCryptInitFunctions failed');
-  end;
-
 var
 var
   Reader: TCompressedBlockReader;
   Reader: TCompressedBlockReader;
 
 
@@ -3161,9 +3146,7 @@ begin
         ReadEntries(seUninstallRun, SetupHeader.NumUninstallRunEntries, SizeOf(TSetupRunEntry),
         ReadEntries(seUninstallRun, SetupHeader.NumUninstallRunEntries, SizeOf(TSetupRunEntry),
           Integer(@PSetupRunEntry(nil).MinVersion),
           Integer(@PSetupRunEntry(nil).MinVersion),
           Integer(@PSetupRunEntry(nil).OnlyBelowVersion));
           Integer(@PSetupRunEntry(nil).OnlyBelowVersion));
-
-        { Wizard image }
-
+        { Wizard images }
         Reader.Read(N, SizeOf(LongInt));
         Reader.Read(N, SizeOf(LongInt));
         for I := 0 to N-1 do
         for I := 0 to N-1 do
           WizardImages.Add(ReadWizardImage(Reader));
           WizardImages.Add(ReadWizardImage(Reader));
@@ -3176,12 +3159,6 @@ begin
           DecompressorDLL := TMemoryStream.Create;
           DecompressorDLL := TMemoryStream.Create;
           ReadFileIntoStream(DecompressorDLL, Reader);
           ReadFileIntoStream(DecompressorDLL, Reader);
         end;
         end;
-        { Decryption DLL }
-        DecryptDLL := nil;
-        if shEncryptionUsed in SetupHeader.Options then begin
-          DecryptDLL := TMemoryStream.Create;
-          ReadFileIntoStream(DecryptDLL, Reader);
-        end;
       finally
       finally
         Reader.Free;
         Reader.Free;
       end;
       end;
@@ -3265,10 +3242,6 @@ begin
   if SetupHeader.CompressMethod in [cmZip, cmBzip] then
   if SetupHeader.CompressMethod in [cmZip, cmBzip] then
     LoadDecompressorDLL;
     LoadDecompressorDLL;
 
 
-  { Extract "_iscrypt.dll" to TempInstallDir, and load it }
-  if shEncryptionUsed in SetupHeader.Options then
-    LoadDecryptDLL;
-
   { Start RestartManager session }
   { Start RestartManager session }
   if InitCloseApplications or
   if InitCloseApplications or
      ((shCloseApplications in SetupHeader.Options) and not InitNoCloseApplications) then begin
      ((shCloseApplications in SetupHeader.Options) and not InitNoCloseApplications) then begin
@@ -3516,10 +3489,6 @@ begin
   if RmSessionStarted then
   if RmSessionStarted then
     RmEndSession(RmSessionHandle);
     RmEndSession(RmSessionHandle);
 
 
-  { Free the _iscrypt.dll handle }
-  if DecryptDLLHandle <> 0 then
-    FreeLibrary(DecryptDLLHandle);
-
   { Free the _isdecmp.dll handle }
   { Free the _isdecmp.dll handle }
   if DecompressorDLLHandle <> 0 then
   if DecompressorDLLHandle <> 0 then
     FreeLibrary(DecompressorDLLHandle);
     FreeLibrary(DecompressorDLLHandle);

+ 0 - 74
Projects/Src/Shared.ArcFour.pas

@@ -1,74 +0,0 @@
-unit Shared.ArcFour;
-
-{
-  Inno Setup
-  Copyright (C) 1997-2004 Jordan Russell
-  Portions by Martijn Laan
-  For conditions of distribution and use, see LICENSE.TXT.
-
-  Interface to ISCrypt.dll (ARCFOUR encryption/decryption)
-}
-
-interface
-
-uses
-  Windows;
-
-type
-  TArcFourContext = record
-    state: array[0..255] of Byte;
-    x, y: Byte;
-  end;
-
-function ArcFourInitFunctions(Module: HMODULE): Boolean;
-procedure ArcFourInit(var Context: TArcFourContext; const Key;
-  KeyLength: Cardinal);
-procedure ArcFourCrypt(var Context: TArcFourContext; const InBuffer;
-  var OutBuffer; Length: Cardinal);
-procedure ArcFourDiscard(var Context: TArcFourContext; Bytes: Cardinal);
-
-implementation
-
-var
-  _ISCryptGetVersion: function: Integer; stdcall;
-  _ArcFourInit: procedure(var context: TArcFourContext; const key;
-    key_length: Cardinal); stdcall;
-  _ArcFourCrypt: procedure(var context: TArcFourContext; const in_buffer;
-    var out_buffer; length: Cardinal); stdcall;
-
-function ArcFourInitFunctions(Module: HMODULE): Boolean;
-begin
-  _ISCryptGetVersion := GetProcAddress(Module, 'ISCryptGetVersion');
-  _ArcFourInit := GetProcAddress(Module, 'ArcFourInit');
-  _ArcFourCrypt := GetProcAddress(Module, 'ArcFourCrypt');
-  if Assigned(_ISCryptGetVersion) and Assigned(_ArcFourInit) and
-     Assigned(_ArcFourCrypt) then begin
-    { Verify that the DLL's version is what we expect }
-    Result := (_ISCryptGetVersion = 1);
-  end
-  else begin
-    Result := False;
-    _ISCryptGetVersion := nil;
-    _ArcFourInit := nil;
-    _ArcFourCrypt := nil;
-  end
-end;
-
-procedure ArcFourInit(var Context: TArcFourContext; const Key;
-  KeyLength: Cardinal);
-begin
-  _ArcFourInit(Context, Key, KeyLength);
-end;
-
-procedure ArcFourCrypt(var Context: TArcFourContext; const InBuffer;
-  var OutBuffer; Length: Cardinal);
-begin
-  _ArcFourCrypt(Context, InBuffer, OutBuffer, Length);
-end;
-
-procedure ArcFourDiscard(var Context: TArcFourContext; Bytes: Cardinal);
-begin
-  _ArcFourCrypt(Context, Pointer(nil)^, Pointer(nil)^, Bytes);
-end;
-
-end.

+ 7 - 1
Projects/Src/Shared.Struct.pas

@@ -33,7 +33,7 @@ const
     this file it's recommended you change SetupID. Any change will do (like
     this file it's recommended you change SetupID. Any change will do (like
     changing the letters or numbers), as long as your format is
     changing the letters or numbers), as long as your format is
     unrecognizable by the standard Inno Setup. }
     unrecognizable by the standard Inno Setup. }
-  SetupID: TSetupID = 'Inno Setup Setup Data (6.3.0)';
+  SetupID: TSetupID = 'Inno Setup Setup Data (6.4.0)';
   UninstallLogID: array[Boolean] of TUninstallLogID =
   UninstallLogID: array[Boolean] of TUninstallLogID =
     ('Inno Setup Uninstall Log (b)', 'Inno Setup Uninstall Log (b) 64-bit');
     ('Inno Setup Uninstall Log (b)', 'Inno Setup Uninstall Log (b) 64-bit');
   MessagesHdrID: TMessagesHdrID = 'Inno Setup Messages (6.0.0) (u)';
   MessagesHdrID: TMessagesHdrID = 'Inno Setup Messages (6.0.0) (u)';
@@ -69,6 +69,11 @@ type
   TSetupLanguageDetectionMethod = (ldUILanguage, ldLocale, ldNone);
   TSetupLanguageDetectionMethod = (ldUILanguage, ldLocale, ldNone);
   TSetupCompressMethod = (cmStored, cmZip, cmBzip, cmLZMA, cmLZMA2);
   TSetupCompressMethod = (cmStored, cmZip, cmBzip, cmLZMA, cmLZMA2);
   TSetupSalt = array[0..7] of Byte;
   TSetupSalt = array[0..7] of Byte;
+  TSetupNonce = record
+    RandomXorStartOffset: Int64;
+    RandomXorFirstSlice: Int32;
+    RemainingRandom: array[0..2] of Int32;
+  end;
   TSetupProcessorArchitecture = (paUnknown, paX86, paX64, paArm32, paArm64);
   TSetupProcessorArchitecture = (paUnknown, paX86, paX64, paArm32, paArm64);
   TSetupProcessorArchitectures = set of TSetupProcessorArchitecture;
   TSetupProcessorArchitectures = set of TSetupProcessorArchitecture;
   TSetupDisablePage = (dpAuto, dpNo, dpYes);
   TSetupDisablePage = (dpAuto, dpNo, dpYes);
@@ -106,6 +111,7 @@ type
     WizardImageAlphaFormat: (afIgnored, afDefined, afPremultiplied); // Must be same as Graphics.TAlphaFormat
     WizardImageAlphaFormat: (afIgnored, afDefined, afPremultiplied); // Must be same as Graphics.TAlphaFormat
     PasswordHash: TSHA1Digest;
     PasswordHash: TSHA1Digest;
     PasswordSalt: TSetupSalt;
     PasswordSalt: TSetupSalt;
+    EncryptionBaseNonce: TSetupNonce;
     ExtraDiskSpaceRequired: Integer64;
     ExtraDiskSpaceRequired: Integer64;
     SlicesPerDisk: Integer;
     SlicesPerDisk: Integer;
     UninstallLogMode: (lmAppend, lmNew, lmOverwrite);
     UninstallLogMode: (lmAppend, lmNew, lmOverwrite);


+ 0 - 141
iscrypt.iss

@@ -1,141 +0,0 @@
-// -- IsCrypt.iss --
-// Include file with support functions to download encryption support
-// Must be included before adding [Files] entries
-//
-#if FileExists('iscrypt-custom.ico')
-  #define iscryptico      'iscrypt-custom.ico'
-  #define iscrypticosizes '[32, 48, 64]'
-#else
-  #define iscryptico      'iscrypt.ico'
-  #define iscrypticosizes '[32]'
-#endif
-//
-[Files]
-Source: "{#iscryptico}"; DestName: "iscrypt.ico"; Flags: dontcopy
-Source: "{tmp}\ISCrypt.dll"; DestDir: "{app}"; Flags: ignoreversion external skipifsourcedoesntexist touch
-
-[Code]
-const
-  ISCryptHash = '2f6294f9aa09f59a574b5dcd33be54e16b39377984f3d5658cda44950fa0f8fc';
-
-var
-  ISCryptPage: TWizardPage;
-  ISCryptCheckBox: TCheckBox;
-
-procedure CreateCustomOption(Page: TWizardPage; ACheckCaption: String; var CheckBox: TCheckBox; PreviousControl: TControl);
-begin
-  CheckBox := TCheckBox.Create(Page);
-  with CheckBox do begin
-    Top := PreviousControl.Top + PreviousControl.Height + ScaleY(12);
-    Width := Page.SurfaceWidth;
-    Height := ScaleY(Height);
-    Anchors := [akLeft, akTop, akRight];
-    Caption := ACheckCaption;
-    Parent := Page.Surface;
-  end;
-end;
-
-function CreateCustomOptionPage(AAfterId: Integer; ACaption, ASubCaption, AIconFileName, ALabel1Caption, ALabel2Caption,
-  ACheckCaption: String; var CheckBox: TCheckBox): TWizardPage;
-var
-  Page: TWizardPage;
-  BitmapImage: TBitmapImage;
-  Label1, Label2: TNewStaticText;
-begin
-  Page := CreateCustomPage(AAfterID, ACaption, ASubCaption);
-  
-  AIconFileName := ExpandConstant('{tmp}\' + AIconFileName);
-  if not FileExists(AIconFileName) then
-    ExtractTemporaryFile(ExtractFileName(AIconFileName));
-
-  BitmapImage := TBitmapImage.Create(Page);
-  with BitmapImage do begin
-    Width := ScaleX(34);
-    Height := ScaleY(34);
-    Parent := Page.Surface;
-  end;
-  
-  InitializeBitmapImageFromIcon(BitmapImage, AIconFileName, Page.SurfaceColor, {#iscrypticosizes});
-
-  Label1 := TNewStaticText.Create(Page);
-  with Label1 do begin
-    AutoSize := False;
-    Left := WizardForm.SelectDirLabel.Left;
-    Width := Page.SurfaceWidth - Left;
-    Anchors := [akLeft, akTop, akRight];
-    WordWrap := True;
-    Caption := ALabel1Caption;
-    Parent := Page.Surface;
-    AdjustHeight;
-  end;
-
-  Label2 := TNewStaticText.Create(Page);
-  with Label2 do begin
-    AutoSize := False;
-    Top := Label1.Top + Label1.Height + ScaleY(12);
-    Width := Page.SurfaceWidth;
-    Anchors := [akLeft, akTop, akRight];
-    WordWrap := True;
-    Caption := ALabel2Caption;
-    Parent := Page.Surface;
-    AdjustHeight;
-  end;
-  
-  CreateCustomOption(Page, ACheckCaption, CheckBox, Label2);
-
-  Result := Page;
-end;
-
-<event('InitializeWizard')>
-procedure IsCryptInitializeWizard;
-var
-  ExistingFileName, Caption, SubCaption1, IconFileName, Label1Caption, Label2Caption, CheckCaption: String;
-begin
-  if WizardForm.PrevAppDir <> '' then begin
-    ExistingFileName := AddBackslash(WizardForm.PrevAppDir) + 'ISCrypt.dll';
-    try
-      if GetSHA256OfFile(ExistingFileName) = ISCryptHash then
-        Exit;
-    except
-    end;
-  end;
-
-  Caption := 'Encryption Support';
-  SubCaption1 := 'Would you like to download encryption support?';
-  IconFileName := 'iscrypt.ico';
-  Label1Caption :=
-    'Inno Setup supports encryption. However, because of encryption import/export laws in some countries, encryption support is not included in the main' +
-    ' Inno Setup installer. Instead, it can be downloaded from a server located in the Netherlands now.';
-  Label2Caption := 'Select whether you would like to download and install encryption support, then click Next.';
-  CheckCaption := '&Download and install encryption support';
-
-  ISCryptPage := CreateCustomOptionPage(wpSelectProgramGroup, Caption, SubCaption1, IconFileName, Label1Caption, Label2Caption, CheckCaption, ISCryptCheckBox);
-  
-  ISCryptCheckBox.Checked := ExpandConstant('{param:downloadiscrypt|0}') = '1';
-end;
-
-<event('NextButtonClick')>
-function IsCryptNextButtonClick(CurPageID: Integer): Boolean;
-var
-  DownloadPage: TDownloadWizardPage;
-begin
-  Result := True;
-  if (CurPageID = wpReady) and (ISCryptCheckBox <> nil) and ISCryptCheckBox.Checked then begin
-    DownloadPage := CreateDownloadPage(SetupMessage(msgWizardPreparing), SetupMessage(msgPreparingDesc), nil);
-    DownloadPage.Clear;
-    DownloadPage.Add('https://jrsoftware.org/download.php/iscrypt.dll', 'ISCrypt.dll', ISCryptHash);
-    DownloadPage.Show;
-    try
-      try
-        DownloadPage.Download;
-      except
-        if DownloadPage.AbortedByUser then
-          Log('Aborted by user.')
-        else
-          SuppressibleMsgBox(AddPeriod(GetExceptionMessage), mbCriticalError, MB_OK, IDOK);
-      end;
-    finally
-      DownloadPage.Hide;
-    end;
-  end;
-end;

+ 2 - 2
isdonateandmail.iss

@@ -2,8 +2,8 @@
 // Include file which adds donate and subscribe buttons to Setup
 // Include file which adds donate and subscribe buttons to Setup
 //
 //
 [Files]
 [Files]
-Source: "isdonate.bmp"; Flags: dontcopy
-Source: "ismail.bmp"; Flags: dontcopy
+Source: "isdonate.bmp"; Flags: dontcopy noencryption
+Source: "ismail.bmp"; Flags: dontcopy noencryption
 
 
 [CustomMessages]
 [CustomMessages]
 ; No need to localize: The IS website is in English only
 ; No need to localize: The IS website is in English only

+ 3 - 3
setup.iss

@@ -6,8 +6,6 @@
 ; Portions Copyright (C) 2000-2024 Martijn Laan. All rights reserved.
 ; Portions Copyright (C) 2000-2024 Martijn Laan. All rights reserved.
 ; For conditions of distribution and use, see LICENSE.TXT.
 ; For conditions of distribution and use, see LICENSE.TXT.
 
 
-#include "iscrypt.iss"
-
 #include "isdonateandmail.iss"
 #include "isdonateandmail.iss"
 
 
 #include "isportable.iss"
 #include "isportable.iss"
@@ -77,7 +75,7 @@ Name: english; MessagesFile: "files\Default.isl"
 #expr FindFiles("files\Languages\")
 #expr FindFiles("files\Languages\")
 
 
 [Messages]
 [Messages]
-HelpTextNote=/PORTABLE=1%nEnable portable mode.%n/DOWNLOADISCRYPT=1%nEnable ISCrypt.dll download.
+HelpTextNote=/PORTABLE=1%nEnable portable mode.
 ; Two "Setup" on the same line looks weird, so put a line break in between
 ; Two "Setup" on the same line looks weird, so put a line break in between
 english.WelcomeLabel1=Welcome to the Inno Setup%nSetup Wizard
 english.WelcomeLabel1=Welcome to the Inno Setup%nSetup Wizard
 
 
@@ -111,6 +109,8 @@ Type: files; Name: "{app}\WizModernImage.bmp"
 Type: files; Name: "{app}\WizModernImage-IS.bmp"
 Type: files; Name: "{app}\WizModernImage-IS.bmp"
 Type: files; Name: "{app}\WizModernSmallImage.bmp"
 Type: files; Name: "{app}\WizModernSmallImage.bmp"
 Type: files; Name: "{app}\WizModernSmallImage-IS.bmp"
 Type: files; Name: "{app}\WizModernSmallImage-IS.bmp"
+; Remove old ISCrypt.dll
+Type: files; Name: "{app}\ISCrypt.dll"
 
 
 [Files]
 [Files]
 Source: "license.txt"; DestDir: "{app}"; Flags: ignoreversion touch
 Source: "license.txt"; DestDir: "{app}"; Flags: ignoreversion touch

+ 1 - 0
whatsnew.htm

@@ -82,6 +82,7 @@ For conditions of distribution and use, see <a href="files/is/license.txt">LICEN
 <p><span class="head2">Other changes</span></p>
 <p><span class="head2">Other changes</span></p>
 <ul>
 <ul>
   <li>Updated the LZMA SDK used by Inno Setup to the latest version, increasing the speed of LZMA and LZMA2 compression and decompression (respectively by 21% and 11% in a test with default settings) without changing the compression ratio. Compression memory requirements have increased by about 4%.</li>
   <li>Updated the LZMA SDK used by Inno Setup to the latest version, increasing the speed of LZMA and LZMA2 compression and decompression (respectively by 21% and 11% in a test with default settings) without changing the compression ratio. Compression memory requirements have increased by about 4%.</li>
+  <li>Updated the encryption algorithm used by Inno Setup to XChaCha20 for extra security. This code is built-in: the separate ISCrypt.dll "encryption module" is no longer used and will be automatically deleted when you update.</li>
   <li>Merged the Inno Setup Preprocessor documentation into the main documentation instead of being separate.</li>
   <li>Merged the Inno Setup Preprocessor documentation into the main documentation instead of being separate.</li>
   <li>Added a dark mode version of the documentation, automatically used by the Compiler IDE if a dark theme is chosen.</li>
   <li>Added a dark mode version of the documentation, automatically used by the Compiler IDE if a dark theme is chosen.</li>
   <li>Pascal Scripting changes:
   <li>Pascal Scripting changes: