PB_AES_Enhancer_& Advanced StringCrypter_with_Compression

Share your advanced PureBasic knowledge/code with the community.
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

PB_AES_Enhancer_& Advanced StringCrypter_with_Compression

Post by Saki »

This new crypter adds the missing convenience functions of AESEncrypter() and AESDecrypter().

When using AES on PB, the data size does not have to be divisible by 16, since so-called cipher stealing is used.
Unfortunately, this is only half implemented.
Data smaller than 16 bytes still need this.
So this advantage is cancelled out again.

Binary key and IV cannot be generated simply from strings,
which makes handling more difficult and encourages nasty errors.

The functions of this crypter are fully compatible to the original PB functions and solve this problems.

These new functions extend the original PB functions without changing them
and add easy to use functions for Key and IV generating.

This new Crypter simplifies the AES handling significantly.

With this new enhanced functions you can use AES as usual,
but the annoying problem if files, data or strings are smaller than 16 bytes is eliminated
and there is no more complicated key or IV generation.


Data lengths from zero upwards are now supported.

Included are :
String to binary key generating for AES-128.
String to binary key generating for AES-256.
String to binary IV generating
Randomized IV generating.
Limitless PB AES function compatible AESEncoder().
Limitless PB AES function compatible AESDecoder().
String to hash keystretching function with progressbar support.

Advanced StringCrypter with automatic randomized IV, Fake StringLength feature,
automatic Integrity check feature and Compression feature.

Supports UTF8 and UTF16.
UTF8 string files are about 50% smaller than standard UTF16 files.
UTF16 is very fast.
If you encrypt the exact same string million times,
each encrypted file will be have a different content.


You can also use these new functions to encrypt blockwise with Progressbar.
Just increase the IV by one with each new block.
So primarily mostly you do not need the StartAESCipher() and AddCipherBuffer() functions.

It doesn't matter now if the data or strings to be encrypted are smaller or larger than 16 bytes, no padding is needed anymore.

The only thing that changes is that now everything is as extremely simple as you probably always wanted it to be.


Keys and IV are usually implemented incorrectly, so that it cries out for a simple universally usable solution.

Note that for data smaller than 16 Bytes Encrypter and Decrypter work the same.
Therefore, a double encryption with the same Key and IV causes a decryption.
This may be difficult to understand at first, but it is absolutely OK and all works very fine.

Base Code - full featured

Code: Select all

DeclareModule PB_AES_Enhancer
  EnableExplicit
  Declare Create_16_Byte_Key(key$, salt$="") ; For AES-128
  Declare Create_32_Byte_Key(key$, salt$="") ; For AES-256
  Declare Create_IV(iv$)                     ; Binary IV
  Declare Create_RND_IV()                    ; Binary randomized IV
  Declare QAESEncoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC) ; Encoder
  Declare QAESDecoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC) ; Decoder
  Declare.s QAESKeyStretching(window_ID, progressbar_ID, key$, salt$="", key_stretching_loops=1e4) ; Key-stretching
EndDeclareModule

Module PB_AES_Enhancer
  ; PB AES Functions compatible Crypter with smaller 16 Bytes Fix - By Saki
  ; < 16 Bytes Encoder and Decoder works at the same
  ; KeyStretching impeded key brute force quite substantial
  UseMD5Fingerprint() : UseSHA3Fingerprint()
  
  Procedure Create_16_Byte_Key(key$, salt$="")
    Protected i, ii, stepp=SizeOf(character)<<1 : Static Dim register.q(1) 
    key$=StringFingerprint(key$+salt$, #PB_Cipher_MD5, 0, #PB_Unicode)
    For i=0 To 15 : PokeB(@register(0)+i, Val("$"+PeekS(@key$+ii, 2))) : ii+stepp : Next
    ProcedureReturn @register.q(0)
  EndProcedure
  
  Procedure Create_32_Byte_Key(key$, salt$="")
    Protected i, ii, stepp=SizeOf(character)<<1 : Static Dim register.q(3)
    key$=StringFingerprint(key$+salt$, #PB_Cipher_SHA3, 256, #PB_Unicode)
    For i=0 To 31 : PokeB(@register(0)+i, Val("$"+PeekS(@key$+ii, 2))) : ii+stepp : Next
    ProcedureReturn @register.q(0)
  EndProcedure
  
  Procedure Create_IV(iv$)
    Protected i, ii, stepp=SizeOf(character)<<1 : Static Dim iv.q(1)
    iv$=StringFingerprint(iv$, #PB_Cipher_MD5, 0, #PB_Unicode)
    For i=0 To 15 : PokeB(@iv(0)+i, Val("$"+PeekS(@iv$+ii, 2))) : ii+stepp : Next
    ProcedureReturn @iv(0)
  EndProcedure
  
  Procedure Create_RND_IV()
    Static Dim iv.q(1) : If OpenCryptRandom() : CryptRandomData(@iv.q(0), 16) : ProcedureReturn @iv.q(0) : EndIf
    ProcedureReturn 0
  EndProcedure
  
  Macro common(coder)
    If size<16
      Protected i, *ii.byte : Dim buffer.q(1) : If modus=#PB_Cipher_CBC : CopyMemory(*iv, @buffer(0), 16) : EndIf
      Protected result=AESEncoder(@buffer(0), @buffer(0), 16, *key, bits, 0, #PB_Cipher_ECB)
      If Not result : ProcedureReturn 0 : EndIf
      ; For i=0 To size-1 : PokeB(*output+i, PeekB(@buffer(0)+i) ! PeekB(*input+i)) : Next : ProcedureReturn size ; simple
      *ii=@buffer(0)
      For i=1 To size : *output\b=*ii\b ! *input\b : *input+1 : *output+1 : *ii+1 : Next
    ProcedureReturn size : Else : ProcedureReturn AES#coder(*input, *output, size, *key, bits, *iv, modus)
    EndIf
  EndMacro
  
  Procedure QAESEncoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC)
    common(Encoder)
  EndProcedure
  
  Procedure QAESDecoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC)
    common(Decoder)
  EndProcedure
  
  Procedure.s QAESKeyStretching(window_ID, progressbar_ID, key$, salt$="", key_stretching_loops=1e4)
    Protected get_time.q , i ; by walbus
    get_time=ElapsedMilliseconds()
    For i=1 To key_stretching_loops ; Iterations
      key$=ReverseString(salt$)+key$+salt$+ReverseString(key$)
      key$=Fingerprint(@key$, StringByteLength(key$), #PB_Cipher_SHA3, 512)
      If IsWindow(window_ID) And IsGadget(progressbar_ID)
        If ElapsedMilliseconds()>get_time+100
          SetGadgetState(progressbar_ID, 100*i/key_stretching_loops)
          get_time.q=ElapsedMilliseconds()
        EndIf
      EndIf
    Next i
    key$=ReverseString(key$)+salt$+key$+ReverseString(key$)
    key$=Fingerprint(@key$, StringByteLength(key$), #PB_Cipher_SHA3, 512) ; Finalize
    If IsGadget(progressbar_ID)
      SetGadgetState(progressbar_ID, 100)
    EndIf
    Delay(100)
    ProcedureReturn key$
  EndProcedure
EndModule
UseModule PB_AES_Enhancer

; ######################### Use the module ##########################
EnableExplicit

Define source$="ABCDEF"; GHIJKLMNOP" ; GHIJKLMNOPQRSTUVWXYZ0123456789" ; You can change how ever you want
Define destination$=Space(Len(source$))

Define password$="Hello Password"
Define iv$="123456"

; With KeyStretching - 1e4 Loops
Define key$=QAESKeyStretching(0, 0, Password$, iv$, 1e4)
Debug "KeyStretching Sample : "+key$
Debug ""
Debug "Bytes encrypted : "+QAESEncoder(@source$, @destination$, StringByteLength(source$), Create_32_Byte_Key(key$), 256, Create_IV(iv$))
Debug destination$
Debug " "
Debug "Bytes decrypted : "+QAESDecoder(@destination$, @source$, StringByteLength(source$), Create_32_Byte_Key(key$), 256, Create_IV(iv$))
Debug source$
Debug ""
; Without KeyStretching
Debug "Bytes encrypted : "+QAESEncoder(@source$, @destination$, StringByteLength(source$), Create_32_Byte_Key(Password$), 256, Create_IV(iv$))
Debug destination$
Debug " "
Debug "Bytes decrypted : "+QAESDecoder(@destination$, @source$, StringByteLength(source$), Create_32_Byte_Key(Password$), 256, Create_IV(iv$))
Debug source$
Code & Advanced StringCrypter

Code: Select all

DeclareModule PB_AES_Enhancer
  EnableExplicit
  Declare Create_16_Byte_Key(key$, salt$="") ; For AES-128
  Declare Create_32_Byte_Key(key$, salt$="") ; For AES-256
  Declare Create_IV(iv$)                     ; Binary IV
  Declare Create_RND_IV()                    ; Binary randomized IV
  Declare QAESEncoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC) ; Encoder
  Declare QAESDecoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC) ; Decoder
  Declare.s QAESKeyStretching(window_ID, progressbar_ID, key$, salt$="", key_stretching_loops=1e4) ; Key-stretching
  
  ; String crypter with automatically randomized IV, fake length, intergrity check and compression - UTF8 and UTF16
  ; UTF16 mode=0 / UTF8 mode=1 - UTF8 files are ~50% smaller - UTF16 is very fast
  ; ..._fake_length - Blur the real string length
  Declare QAES_Write_encrypted_string_file(string$, destination_path$, key$, mode=0, min_fake_length=0, max_fake_length=0, check=0, compressing=0) ; WriteFile fail = -1
  Declare.s QAES_Read_encrypted_string_file(destination_path$, key$, mode=0, check=0, compressing=0)
  Declare QAESCheckIntegrity()           ; Check the integrity of the string after decrypting a string file
  Declare QAESCheckDecrypter()           ; Check the string decrypter state after decrypting a string file - ReadFile fail = -1
  Declare QAESGetCompressedDataPercent() ; Get the compressed size as percent
  Declare QAESUseHash(Hash_length_=32)   ; You can set a integrity check hash from 4 to 32Bytes length (Preset=32)
  Declare.s QAESGetUsedPath()            ; Get the used path
EndDeclareModule

Module PB_AES_Enhancer
  ; PB AES Functions compatible Crypter with smaller 16 Bytes Fix - By Saki
  ; With advanced string crypter
  ; < 16 Bytes Encoder and Decoder works at the same
  ; KeyStretching impeded key brute force quite substantial
  
  UseCRC32Fingerprint() : UseMD5Fingerprint() : UseSHA3Fingerprint() : UseLZMAPacker()
  
  Global QAESCheckIntegrity, QAESCheckDecrypter
  Global QAESGetCompressedDataPercent, Hash_length=32, QAESGetUsedPath$
  
  Procedure.s GetCheckHash(string$)
    If Hash_length>16
      ProcedureReturn StringFingerprint(string$, #PB_Cipher_SHA3, 256, #PB_Unicode)
    ElseIf Hash_length>4
      ProcedureReturn StringFingerprint(string$, #PB_Cipher_MD5, 0, #PB_Unicode)
    EndIf
    ProcedureReturn StringFingerprint(string$, #PB_Cipher_CRC32, 0, #PB_Unicode)
  EndProcedure
  
  Procedure Create_16_Byte_Key(key$, salt$="")
    Protected i, ii, stepp=SizeOf(character)<<1 : Static Dim register.q(1) 
    key$=StringFingerprint(key$+salt$, #PB_Cipher_MD5, 0, #PB_Unicode)
    For i=0 To 15 : PokeB(@register(0)+i, Val("$"+PeekS(@key$+ii, 2))) : ii+stepp : Next
    ProcedureReturn @register.q(0)
  EndProcedure
  
  Procedure Create_32_Byte_Key(key$, salt$="")
    Protected i, ii, stepp=SizeOf(character)<<1 : Static Dim register.q(3)
    key$=StringFingerprint(key$+salt$, #PB_Cipher_SHA3, 256, #PB_Unicode)
    For i=0 To 31 : PokeB(@register(0)+i, Val("$"+PeekS(@key$+ii, 2))) : ii+stepp : Next
    ProcedureReturn @register.q(0)
  EndProcedure
  
  Procedure Create_IV(iv$)
    Protected i, ii, stepp=SizeOf(character)<<1 : Static Dim iv.q(1)
    iv$=StringFingerprint(iv$, #PB_Cipher_MD5, 0, #PB_Unicode)
    For i=0 To 15 : PokeB(@iv(0)+i, Val("$"+PeekS(@iv$+ii, 2))) : ii+stepp : Next
    ProcedureReturn @iv(0)
  EndProcedure
  
  Procedure Create_RND_IV()
    Static Dim iv.q(1) : If OpenCryptRandom() : CryptRandomData(@iv.q(0), 16) : ProcedureReturn @iv.q(0) : EndIf
    ProcedureReturn 0
  EndProcedure
  
  Macro common(coder)
    If size<16
      Protected i, *ii.byte : Dim buffer.q(1)
      Protected result=AESEncoder(@buffer(0), @buffer(0), 16, *key, bits, *iv, #PB_Cipher_CBC)
      If Not result : ProcedureReturn 0 : EndIf
      ; For i=0 To size-1 : PokeB(*output+i, PeekB(@buffer(0)+i) ! PeekB(*input+i)) : Next : ProcedureReturn size ; simple
      *ii=@buffer(0) : For i=1 To size : *output\b=*ii\b ! *input\b : *input+1 : *output+1 : *ii+1 : Next
    ProcedureReturn size : Else : ProcedureReturn AES#coder(*input, *output, size, *key, bits, *iv, modus)
    EndIf
  EndMacro
  
  Procedure QAESEncoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC)
    common(Encoder)
  EndProcedure
  
  Procedure QAESDecoder(*input.byte, *output.byte, size, *key, bits, *iv, modus=#PB_Cipher_CBC)
    common(Decoder)
  EndProcedure
  
  Procedure.s QAESKeyStretching(window_ID, progressbar_ID, key$, salt$="", key_stretching_loops=1e4)
    Protected get_time.q , i ; by walbus
    get_time=ElapsedMilliseconds()
    For i=1 To key_stretching_loops ; Iterations
      key$=ReverseString(salt$)+key$+salt$+ReverseString(key$)
      key$=Fingerprint(@key$, StringByteLength(key$), #PB_Cipher_SHA3, 512)
      If IsWindow(window_ID) And IsGadget(progressbar_ID)
        If ElapsedMilliseconds()>get_time+100
          SetGadgetState(progressbar_ID, 100*i/key_stretching_loops)
          get_time.q=ElapsedMilliseconds()
        EndIf
      EndIf
    Next i
    key$=ReverseString(key$)+salt$+key$+ReverseString(key$)
    key$=Fingerprint(@key$, StringByteLength(key$), #PB_Cipher_SHA3, 512) ; Finalize
    If IsGadget(progressbar_ID)
      SetGadgetState(progressbar_ID, 100)
    EndIf
    Delay(100)
    ProcedureReturn key$
  EndProcedure

  Procedure QAES_Write_encrypted_string_file(string$, destination_path$, key$, mode=0, min_fake_length=0, max_fake_length=0, check=0, compressing=0)
    QAESGetCompressedDataPercent=0
    QAESGetUsedPath$=destination_path$
    Protected QAESGetCompressedSize, QAESGetUncompressedSize
    Protected file=CreateFile(#PB_Any, destination_path$), *buffer, *buffer_1, *buffer_2, i, ii, stepp=SizeOf(character)<<1
    If Not file : ProcedureReturn -1 : EndIf
    If min_fake_length<0 : min_fake_length=0 : EndIf
    If max_fake_length<0 : max_fake_length=0 : EndIf
    Protected Dim iv.q(1), seed.q
    If OpenCryptRandom()
      If min_fake_length>0 Or max_fake_length>0
        CryptRandomData(@seed, 8)
      EndIf 
      CryptRandomData(@iv(0), 16)
    Else
      If min_fake_length>0 Or max_fake_length>0
        RandomData(@seed, 8)
      EndIf 
      RandomData(@iv(0), 16)
    EndIf
    Protected *iv=@iv(0), *key=Create_32_Byte_Key(key$)
    RandomSeed(seed)
    If mode=1
      *buffer=UTF8(string$)
      If Not *buffer : ProcedureReturn -2 : EndIf
      If seed
        If min_fake_length>max_fake_length : max_fake_length=min_fake_length : EndIf
        Protected rnd_fake_length=Random(max_fake_length, min_fake_length)
        *buffer_1=AllocateMemory(MemorySize(*buffer)+rnd_fake_length)
        If rnd_fake_length>0
          RandomData(*buffer_1+MemorySize(*buffer_1)-rnd_fake_length, rnd_fake_length)
        EndIf
        If Not *buffer_1 : FreeMemory(*buffer) : ProcedureReturn -3 : EndIf
        CopyMemory(*buffer, *buffer_1, MemorySize(*buffer))
        Swap *buffer, *buffer_1
        FreeMemory(*buffer_1)
      EndIf
    Else
      If seed
        If min_fake_length>max_fake_length : max_fake_length=min_fake_length : EndIf
        Protected StringByteLength_=StringByteLength(string$)
        *buffer=AllocateMemory(StringByteLength_+1+rnd_fake_length)
        If rnd_fake_length>0
          RandomData(*buffer+StringByteLength_+1-rnd_fake_length, rnd_fake_length)
        EndIf
      Else
        *buffer=AllocateMemory(StringByteLength(string$)+1)
      EndIf
      If Not *buffer : ProcedureReturn -4 : EndIf
    EndIf
    If Not mode
      CopyMemory(@string$, *buffer, MemorySize(*buffer)-1)
    EndIf
    *buffer_2=AllocateMemory(MemorySize(*buffer))
    If Not *buffer_2 : FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -5 : EndIf
    
    Protected Dim compressed.q(1)
    
    QAESGetUncompressedSize=MemorySize(*buffer)-1
    
    If compressing
      compressed(0)=CompressMemory(*buffer, MemorySize(*buffer), *buffer_2, MemorySize(*buffer))
      QAESGetCompressedSize=compressed(0)
    EndIf
    If Not compressed(0) : CopyMemory(*buffer, *buffer_2, MemorySize(*buffer)) : EndIf
    
    If QAESGetUncompressedSize>0
      If QAESGetCompressedSize>QAESGetUncompressedSize
        QAESGetCompressedDataPercent=-1
      Else
        QAESGetCompressedDataPercent=100/QAESGetUncompressedSize*QAESGetCompressedSize
      EndIf
    EndIf
    
    If compressed(0)
      compressed(1)=MemorySize(*buffer)
      If Not QAESEncoder(*buffer_2, *buffer, compressed(0), *key, 256, *iv, #PB_Cipher_CBC)
        FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -6
      EndIf
      If WriteData(file, *buffer, compressed(0))<>compressed(0)
        FreeMemory(*buffer) : FreeMemory(*buffer_2) : CloseFile(file) : ProcedureReturn -7
      EndIf
    Else
      If Not QAESEncoder(*buffer, *buffer_2, MemorySize(*buffer), *key, 256, *iv, #PB_Cipher_CBC)
        FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -8
      EndIf
      If WriteData(file, *buffer_2, MemorySize(*buffer))<>MemorySize(*buffer)
        FreeMemory(*buffer) : FreeMemory(*buffer_2) : CloseFile(file) : ProcedureReturn -9
      EndIf  
    EndIf    
    
    FreeMemory(*buffer_2)
    If WriteData(file, @iv(0), 16)<>16 : FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -10 : EndIf
    FreeMemory(*buffer)
    If check
      Protected hash$=GetCheckHash(string$)
      *buffer=AllocateMemory(Hash_length)
      If Not *buffer : CloseFile(file) : ProcedureReturn -11 : EndIf
      For i=0 To Hash_length-1 : PokeB(*buffer+i, Val("$"+PeekS(@hash$+ii, 2))) : ii+stepp : Next
      
      If Hash_length>16
        If Not QAESEncoder(*buffer, *buffer, 16, *key, 256, *iv, #PB_Cipher_CBC)
          FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -12
        EndIf
        
        If Not QAESEncoder(*buffer+16, *buffer+16, Hash_length-16, *key, 256, *iv, #PB_Cipher_CBC)
          FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -13
        EndIf
      Else
        If Not QAESEncoder(*buffer, *buffer, Hash_length, *key, 256, *iv, #PB_Cipher_CBC)
          FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -14
        EndIf
      EndIf
      
      If WriteData(file, *buffer, Hash_length)<>Hash_length : FreeMemory(*buffer) : CloseFile(file) : ProcedureReturn -15 : EndIf
      FreeMemory(*buffer)
    EndIf
    
    If compressing
      If Not QAESEncoder(@compressed(0), @compressed(0), 16, *key, 256, *iv, #PB_Cipher_CBC)
        CloseFile(file) : ProcedureReturn -16
      EndIf
      If WriteData(file, @compressed(0), 16)<>16 : CloseFile(file) : ProcedureReturn -17 : EndIf ; ###############
    EndIf
    
    CloseFile(file)
    Create_32_Byte_Key(" ") ; Clear from ram after processing
    ProcedureReturn 1
  EndProcedure
  
  Procedure.s QAES_Read_encrypted_string_file(destination_path$, key$, mode=0, check=0, compressing=0)
    QAESGetCompressedDataPercent=0
    QAESGetUsedPath$=destination_path$
    Protected QAESGetCompressedSize, QAESGetUncompressedSize 
    Protected compress_offset, *buffer, *buffer_1, *buffer_2, i, ii, stepp=SizeOf(character)<<1 
    If compressing : compress_offset=16 : EndIf
    
    Macro FreeMem
      If *buffer : FreeMemory(*buffer) : EndIf
      If *buffer_1 : FreeMemory(*buffer_1) : EndIf
    EndMacro
    
    Protected file=ReadFile(#PB_Any, destination_path$)
    If Not file : QAESCheckDecrypter=-1 : ProcedureReturn "" : EndIf
    If file
      If Not Lof(file)=>16+check+compress_offset : QAESCheckDecrypter=-2 : ProcedureReturn "" : EndIf
      If check : check=Hash_length : EndIf
      Protected Dim compressed.q(1), Dim iv.q(1)
      
      FileSeek(file, Lof(file)-16-check-compress_offset)
      If ReadData(file, @iv(0), 16)<>16 : QAESCheckDecrypter=-3 : CloseFile(file) : ProcedureReturn "" : EndIf 
      Protected *iv=@iv(0), *key=Create_32_Byte_Key(key$)
      
      If compressing
        FileSeek(file, Lof(file)-compress_offset)
        If ReadData(file, @compressed(0), 16)<>16 : QAESCheckDecrypter=-4 : CloseFile(file) : ProcedureReturn "" : EndIf
        If Not QAESDecoder(@compressed(0), @compressed(0), 16, *key, 256, *iv, #PB_Cipher_CBC)
          QAESCheckDecrypter=-5 : ProcedureReturn ""
        EndIf  
        If PeekW(@compressed(0)+6) Or PeekW(@compressed(1)+6)
          CloseFile(file) : QAESCheckDecrypter=-6 : ProcedureReturn ""
        EndIf
      EndIf
      
      If check
        *buffer_1=AllocateMemory(Hash_length)
        If Not *buffer_1 : CloseFile(file) : QAESCheckDecrypter=-7 : ProcedureReturn "" : EndIf
        FileSeek(file, Lof(file)-check-compress_offset)
        If ReadData(file, *buffer_1, Hash_length)<>Hash_length : FreeMem : QAESCheckDecrypter=-8 : CloseFile(file) : ProcedureReturn "" : EndIf
      EndIf
      If compressed(0)
        *buffer=AllocateMemory(compressed(0))
      Else
        *buffer=AllocateMemory(Lof(file)-16-check-compress_offset)
      EndIf
      If Not *buffer : FreeMem : QAESCheckDecrypter=-9 : ProcedureReturn "" : EndIf
      FileSeek(file, 0)
      If ReadData(file, *buffer, Lof(file)-16-check-compress_offset)<>Lof(file)-16-check-compress_offset
        QAESCheckDecrypter=-10 : FreeMem : CloseFile(file) : ProcedureReturn ""
      EndIf
      If compressed(0)
        *buffer_2=AllocateMemory(compressed(1))
      Else
        *buffer_2=AllocateMemory(MemorySize(*buffer))
      EndIf
      If Not *buffer_2 : FreeMem : QAESCheckDecrypter=-11 : CloseFile(file) : ProcedureReturn "" : EndIf
      If Not QAESDecoder(*buffer, *buffer_2, MemorySize(*buffer), *key, 256, *iv, #PB_Cipher_CBC)
        FreeMem : QAESCheckDecrypter=-12 : FreeMemory(*buffer_2) : CloseFile(file): ProcedureReturn ""
      EndIf
      
      QAESGetUncompressedSize=compressed(1)
      
      If compressed(0)
        FreeMemory(*buffer)
        *buffer=AllocateMemory(compressed(1))
        QAESGetCompressedSize=UncompressMemory(*buffer_2, compressed(0), *buffer, compressed(1))
        If QAESGetCompressedSize<1
          QAESGetCompressedSize=0 : FreeMem : QAESCheckDecrypter=-13 : FreeMemory(*buffer_2) : CloseFile(file) : ProcedureReturn ""
        EndIf
      EndIf
      
      If QAESGetUncompressedSize>0
        If QAESGetCompressedSize>QAESGetUncompressedSize
          QAESGetCompressedDataPercent=-1
        Else
          QAESGetCompressedDataPercent=100/QAESGetUncompressedSize*QAESGetCompressedSize
        EndIf
      EndIf
      
      If mode=1
        If compressed(0)
          Protected string$=PeekS(*buffer, -1, #PB_UTF8)
        Else
          string$=PeekS(*buffer_2, -1, #PB_UTF8)
        EndIf
      Else
        If compressed(0)
          string$=Space(compressed(1))
          CopyMemory(*buffer, @string$, compressed(1))
        Else
          string$=Space(MemorySize(*buffer_2))
          CopyMemory(*buffer_2, @string$, MemorySize(*buffer))
        EndIf
      EndIf
      FreeMemory(*buffer)
      FreeMemory(*buffer_2)
      CloseFile(file)
      If check
        Protected hash$=GetCheckHash(string$)
        *buffer=AllocateMemory(Hash_length)
        If Not *buffer : QAESCheckDecrypter=-14 : FreeMem : ProcedureReturn "" : EndIf
        For i=0 To Hash_length-1 : PokeB(*buffer+i, Val("$"+PeekS(@hash$+ii, 2))) : ii+stepp : Next
        
        If Hash_length>16 
          If Not QAESDEcoder(*buffer_1, *buffer_1, 16, *key, 256, *iv, #PB_Cipher_CBC)
            FreeMem : QAESCheckDecrypter=-15 : ProcedureReturn ""
          EndIf
          
          If Not QAESDecoder(*buffer_1+16, *buffer_1+16, Hash_length-16, *key, 256, *iv, #PB_Cipher_CBC)
            FreeMem : QAESCheckDecrypter=-16 : ProcedureReturn ""
          EndIf
        Else
          If Not QAESDEcoder(*buffer_1, *buffer_1, Hash_length, *key, 256, *iv, #PB_Cipher_CBC)
            FreeMem : QAESCheckDecrypter=-17 : ProcedureReturn ""
          EndIf
        EndIf 
        
        QAESCheckIntegrity=CompareMemory(*buffer, *buffer_1, MemorySize(*buffer))
        FreeMemory(*buffer)
      EndIf
      Create_32_Byte_Key(" ") ; Clear from ram after processing
    EndIf
    QAESCheckDecrypter=1 
    ProcedureReturn string$
  EndProcedure
  
  Procedure QAESCheckIntegrity()
    ProcedureReturn QAESCheckIntegrity
  EndProcedure
  
  Procedure QAESCheckDecrypter()
    ProcedureReturn QAESCheckDecrypter
  EndProcedure
  
  Procedure QAESGetCompressedDataPercent()
    ProcedureReturn QAESGetCompressedDataPercent
  EndProcedure
  
  Procedure QAESUseHash(Hash_length_=32)
    If Hash_length_>3 And Hash_length_<33
      Hash_length=Hash_length_
      ProcedureReturn Hash_length
    Else
      MessageRequester(" QAESUseHash", "Set 4 > 32")
      End
    EndIf
  EndProcedure
  
  Procedure.s QAESGetUsedPath()
    ProcedureReturn QAESGetUsedPath$
  EndProcedure
  
EndModule
UseModule PB_AES_Enhancer

; ######################### Use the module ##########################
EnableExplicit

QAESUseHash(16) ; You can set a integrity check hash from 4 to 32Bytes length (Preset=32) 

Define source$="ABCDEFaaaaaaaaaaaaaaaaaaaaaaaaaaaaaX"; GHIJKLMNOP" ; GHIJKLMNOPQRSTUVWXYZ0123456789" ; You can change how ever you want
Define destination$=Space(Len(source$))

Define password$="Hello Password"
Define iv$="123456"

; With KeyStretching - 1e4 Loops
Define key$=QAESKeyStretching(0, 0, Password$, iv$, 1e4)
Debug "KeyStretching Sample : "+key$
Debug ""
Debug "Bytes encrypted : "+QAESEncoder(@source$, @destination$, StringByteLength(source$), Create_32_Byte_Key(key$), 256, Create_IV(iv$))
Debug destination$
Debug " "
Debug "Bytes decrypted : "+QAESDecoder(@destination$, @source$, StringByteLength(source$), Create_32_Byte_Key(key$), 256, Create_IV(iv$))
Debug source$
Debug ""
; Without KeyStretching
Debug "Bytes encrypted : "+QAESEncoder(@source$, @destination$, StringByteLength(source$), Create_32_Byte_Key(Password$), 256, Create_IV(iv$))
Debug destination$
Debug " "
Debug "Bytes decrypted : "+QAESDecoder(@destination$, @source$, StringByteLength(source$), Create_32_Byte_Key(Password$), 256, Create_IV(iv$))
Debug source$
Debug ""
; String to file crypting
Define mode=1 ; 0=UTF16 - 1=UTF8
Define compressing=1 ; Use compression
Define path$=GetUserDirectory(#PB_Directory_Desktop)+"Encrypted_String"
Debug "String encrypt State  : "+QAES_Write_encrypted_string_file(source$, path$, password$, mode, 0, 0, 0, compressing)
Debug QAESGetUsedPath() ; Get the used path
Debug "Compressed data size as percent "+QAESGetCompressedDataPercent() ; Get the compressed size as percent
Debug QAES_Read_encrypted_string_file(path$, password$, mode, 0, compressing)
Debug " "
; String to file crypting - Randomized length
Define min_fake_length=5
Define max_fake_length=30
Define mode=0 ; 0=UTF16 - 1=UTF8
Define path$=GetUserDirectory(#PB_Directory_Desktop)+"Encrypted_String_Randomized_length"
Debug "String encrypt State : "+QAES_Write_encrypted_string_file(source$, path$, password$, mode, min_fake_length, max_fake_length, 0, compressing)
Debug QAESGetUsedPath() ; Get the used path
Debug "Compressed data size as percent "+QAESGetCompressedDataPercent() ; Get the compressed size as percent
Debug "String decrypted  : "
Debug QAES_Read_encrypted_string_file(path$, password$, mode, 0, compressing)
Debug " "
; String to file crypting - Randomized length - String Check
Define check=1
Define mode=1 ; 0=UTF16 - 1=UTF8
Define path$=GetUserDirectory(#PB_Directory_Desktop)+"Encrypted_String_Randomized_length_and_Check"
Debug "String encrypt State  : "+QAES_Write_encrypted_string_file(source$, path$, password$, mode, min_fake_length, max_fake_length, check, compressing)
Debug QAESGetUsedPath() ; Get the used path
Debug "Compressed data size as percent "+QAESGetCompressedDataPercent() ; Get the compressed size as percent
Debug "String decrypted  : "
Debug QAES_Read_encrypted_string_file(path$, password$, mode, check, compressing)
Debug "State Decrypter after decrypting : "+QAESCheckDecrypter() ; Check the string decrypter state after decrypting a string file
Debug "State decrypted String integrity check : "+QAESCheckIntegrity() ; Check the integrity of the string after decrypting a string file
Last edited by Saki on Fri Mar 05, 2021 8:01 pm, edited 30 times in total.
地球上の平和
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by Saki »

Code significantly enhanced

Look the first post.

Have fun
Last edited by Saki on Tue Mar 02, 2021 8:23 pm, edited 1 time in total.
地球上の平和
User avatar
DoubleDutch
Addict
Addict
Posts: 3219
Joined: Thu Aug 07, 2003 7:01 pm
Location: United Kingdom
Contact:

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by DoubleDutch »

Thanks. It would be good if @Fred added these to the official pb lib.
https://deluxepixel.com <- My Business website
https://reportcomplete.com <- School end of term reports system
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by Saki »

Hi, yes, it really wouldn't be bad if something was improved there.
Many users have an extremely hard time with it.
Last edited by Saki on Thu Feb 25, 2021 1:08 am, edited 2 times in total.
地球上の平和
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by Kwai chang caine »

Works here thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: PB_AES_Enhancer_with_advanced_StringCrypter

Post by Saki »

Thank you DoubleDutch and Kwai

Please get the new Codes above.

I think now all ist final.
地球上の平和
User avatar
Keya
Addict
Addict
Posts: 1891
Joined: Thu Jun 04, 2015 7:10 am

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by Keya »

Saki could you please give a description of your padding technique? it's critical. Thankyou :)
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by Saki »

Codes updated - a little

Hi Keya, no problem.

https://en.wikipedia.org/wiki/Ciphertext_stealing
Last edited by Saki on Wed Mar 03, 2021 10:46 pm, edited 1 time in total.
地球上の平和
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by Kwai chang caine »

I tested the 2 new codes, and that works too :D
Thanks again for sharing your works 8)
ImageThe happiness is a road...
Not a destination
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: PB_AES_Enhancer_with advanced StringCrypter

Post by Saki »

Many thanks again Kwai.

The advanced code is optimized and extended again.
The String Crypter meets all current security standards.

Remember that you cannot use the String Crypter with the original
PB AES calls.

But you can simply replace the original calls in your code with the QAES calls, then you can use it.

Well, I'm curious to see how the new module will go on.

Hint : You can look from time to time for new codes, i announce this mostly not if there are no compatibility changes.

Have fun with it.
地球上の平和
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: PB_AES_Enhancer_& Advanced StringCrypter_with_Compressi

Post by Saki »

Code 2 updated

The automatic authentication of encrypted strings is done by an included AES encrypted hash.
You can set the length of this hash between 4 and 32 bytes.
Depending on the setting a CRC32, a MD5 or a SHA3 hash is used automatically.

You can replace the used LZMA compression module with another one.

The use of an MD5 is sufficient for authentication.
It may not be so easy to understand, but this is different from the md5 password hashes, which you are not supposed to use anymore.

If you set the hash length above 16, a SHA3 hash is used automatically.

The smallest and fastest hash for automatic authentication is the CRC32.
This can also be used primarily, encrypted files then become correspondingly smaller.

The protective effect of a hash during the integrity check is higher by a factor of 16 for CBC-encrypted files.

If compressed files are damaged, they can usually no longer be read.
Encrypted texts which should still be readable even if damaged, depending on the damage, must therefore not be compressed.
Text up to a size of 32 bytes cannot be compressed.
Compressed text has a minimum size of 48 bytes.
Compression usually completely eliminates size differences between UTF8 and UTF16.
地球上の平和
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: PB_AES_Enhancer_& Advanced StringCrypter_with_Compressi

Post by Saki »

Update code 2

Now all is final
地球上の平和
Quin
Enthusiast
Enthusiast
Posts: 283
Joined: Thu Mar 31, 2022 7:03 pm
Location: United States
Contact:

Re: PB_AES_Enhancer_& Advanced StringCrypter_with_Compression

Post by Quin »

This works amazingly, thanks so much!
PB v5.40/6.10, Windows 10 64-bit.
16-core AMD Ryzen 9 5950X, 128 GB DDR5.
Post Reply