stcarrez / ada-util

Ada Utility Library - Composing streams, processes, logs, serialization, encoders and more
Apache License 2.0
69 stars 14 forks source link

128Bit AES-CTR Encoding doesn't work #50

Closed adezxc closed 7 months ago

adezxc commented 7 months ago

Utilada version: 2.6.0

I have a simple utility that should be able to encrypt and decrypt files in AES-CTR 128Bit mode, but it fails and returns this CONSTRAINT_ERROR.

Constraint error in To_Unsigned_32

raised CONSTRAINT_ERROR : util-encoders-aes.adb:677 index check failed
[./bin/ada_aes]
0x59615ef1c835 Util.Encoders.Aes.Transform at util-encoders-aes.adb:677
0x59615ef717dd Util.Streams.Aes.Encoding.Write at util-streams-buffered-encoders.adb:83
0x59615ef7c2f7 Util.Streams.Copy at util-streams.adb:37
0x59615ef12313 Ada_Aes.Encrypt_File at ada_aes.adb:63
0x59615ef12e82 Ada_Aes at ada_aes.adb:84
0x59615ef1376c Main at b__ada_aes.adb:469
[/usr/lib/libc.so.6]
0x7697fe158cce
0x7697fe158d88
[./bin/ada_aes]
0x59615ef11bd3 _start at ???
0xfffffffffffffffe

With some debugging, it seems that it manages to go through one 4KB data block, then Offset is set to 15 and DataLast index is16`, so it tries to access a non-existent element

I will add the program's source code in the comment

adezxc commented 7 months ago
with Util.Encoders.AES;
with Util.Streams;
with Util.Streams.Files;
with Util.Streams.AES;
with Util.Encoders;
with Ada.Command_Line;
with Text_IO; use Text_IO;
with Ada.Characters.Handling;
with Ada.Streams.Stream_IO;
with Util.Streams.Texts;

procedure Ada_Aes is
   use Ada.Command_Line;
   function Base32_Encode (S : String) return String is
      use Util.Encoders;
      C : constant Encoder := Util.Encoders.Create ("base32");
   begin
      return C.Encode ((S));
   end Base32_Encode;

   function Base32_Decode (S : String) return String is
      use Ada.Characters.Handling;
      use Util.Encoders;
      D : constant Decoder := Util.Encoders.Create ("base32");
   begin
      return D.Decode (To_Upper (S));
   end Base32_Decode;

   procedure Decrypt_File (Source : String; Destination : String; Key : String)
   is
      In_Stream    : aliased Util.Streams.Files.File_Stream;
      Out_Stream   : aliased Util.Streams.Files.File_Stream;
      Decipher     : aliased Util.Streams.AES.Decoding_Stream;
      Password_Key :
        constant Util.Encoders.Secret_Key
          (Length => Util.Encoders.AES.AES_128_Length) :=
        Util.Encoders.Create (Base32_Decode (Key));
   begin
      In_Stream.Open (Ada.Streams.Stream_IO.In_File, Source);
      Out_Stream.Create
        (Mode => Ada.Streams.Stream_IO.Out_File, Name => Destination);
      Decipher.Produces
        (Output => Out_Stream'Unchecked_Access, Size => 65_536);
      Decipher.Set_Key (Secret => Password_Key, Mode => Util.Encoders.AES.CTR);
      Util.Streams.Copy (From => In_Stream, Into => Decipher);
   end Decrypt_File;

   procedure Encrypt_File (Source : String; Destination : String; Key : String)
   is
      In_Stream    : aliased Util.Streams.Files.File_Stream;
      Out_Stream   : aliased Util.Streams.Files.File_Stream;
      Cipher       : aliased Util.Streams.AES.Encoding_Stream;
      Password_Key :
        constant Util.Encoders.Secret_Key
          (Length => Util.Encoders.AES.AES_128_Length) :=
        Util.Encoders.Create (Base32_Decode (Key));
   begin
      In_Stream.Open (Ada.Streams.Stream_IO.In_File, Source);
      Out_Stream.Create
        (Mode => Ada.Streams.Stream_IO.Out_File, Name => Destination);
      Cipher.Produces (Output => Out_Stream'Unchecked_Access, Size => 65_536);
      Cipher.Set_Key (Secret => Password_Key, Mode => Util.Encoders.AES.CTR);
      Util.Streams.Copy (From => In_Stream, Into => Cipher);
   end Encrypt_File;
begin
   if Argument_Count /= 4 then
      Put_Line ("Correct usage:");
      Put_Line
        (Ada.Command_Line.Command_Name &
         " encode input.dat output.dat <base32encodedkey>");
      Put_Line
        (Ada.Command_Line.Command_Name &
         " decode input.dat output.dat <base32encodedkey>");
      return;
   end if;

   declare
      Cipher_Mode : String := Argument (1);
      Input_File  : String := Argument (2);
      Output_File : String := Argument (3);
      Key         : String := Argument (4);
   begin
      if Cipher_Mode = "encode" then
         Encrypt_File
           (Source => Input_File, Destination => Output_File, Key => Key);
         return;
      end if;
      if Cipher_Mode = "decode" then
         Decrypt_File
           (Source => Input_File, Destination => Output_File, Key => Key);
         return;
      end if;
      Put_Line ("Unrecognized function");
      return;
   end;
end Ada_Aes;
stcarrez commented 7 months ago

Thanks to have found that issue!

stcarrez commented 7 months ago

CFB encryption has the same issue.

stcarrez commented 7 months ago

The unit tests are wrong and don't test correctly the CFB, OFB and CTR modes.

The tests in regtests/util-streams-tests.adb instantiate a generic procedure with the encryption mode but for the CFB, OFB and CTR tests, they are instantiated with AEC.PCBC mode!!!!!

Fixing these tests, raises the issue!