CSV special crypter QAES - Text>File - File>Text [Module]

Share your advanced PureBasic knowledge/code with the community.
walbus
Addict
Addict
Posts: 929
Joined: Sat Mar 02, 2013 9:17 am

CSV special crypter QAES - Text>File - File>Text [Module]

Post by walbus »

Since it is a big problem to protect CSV files effectively, I give you here a special crypter for it

The Crypter is AES-256 based, very fast, simplest to use, rock stable and does not create any temporary files.

The crypter always creates completely different encrypted files from the same texts.
This works similar to the CBC mode with a continuously changing IV.
But it does not have the weaknesses of CBC encryption.

For easy further processing, the decrypter provides the file directly line by line in a list.

He can be more than helpful :wink:

Code: Select all

DeclareModule QAES_smart_universal_coder
  
  Declare QAES_smart_universal_coder(mode, *buffer_in.word, *buffer_out.word, bytes.q, key$, counter.q=0)
  
EndDeclareModule

Module QAES_smart_universal_coder
  
  EnableExplicit
  
  UseSHA3Fingerprint()
  
  ;- QAES AES256 OFB mode two stage special coder for all things - binarys - strings - text files --------------------
  
  ; This coder can handle automatic string termination for PB strings in compiler mode ASCII and UNICODE
  
  ; The coder works with all data lengths
  
  ; With mode ASCII you can encrypt mixed data, string and binary - This ignore the encryption from zero bytes
  
  ; The coder go ever forward, a extra decoder is unnecessary !
  ; You cipher a file blockwise, set ever the current block number (consecutive) with the counter - Important !

  ; Author Werner Albus - www.nachtoptik.de - www.quick-aes-256.de
  ; No warranty whatsoever - Use at your own risk

  Procedure QAES_smart_universal_coder(mode, *buffer_in.word, *buffer_out.word, bytes.q, key$, counter.q=0)
    
    If Not bytes.q Or key$="" : ProcedureReturn 0 : EndIf
    
    Protected.q i, swap_
    Protected ii, iii, bytes_minus_x, stepp=SizeOf(character)<<1
    Protected *buffer_in_asc.ascii, *buffer_out_asc.ascii
    Protected hash$=key$+Str(counter)+"t8690352cj2p1ch7fgw34uotmq09745$%()=)&%" ; + Salt, you can change - make not to small
    Static fixed_key_string${64}
    Static Dim register.q(3)
    
    fixed_key_string$=Fingerprint(@hash$, StringByteLength(hash$), #PB_Cipher_SHA3, 256)
    
    If mode=2
      bytes_minus_x=bytes-3
    Else
      bytes_minus_x=bytes-2
      *buffer_in_asc.ascii=*buffer_in
      *buffer_out_asc.ascii=*buffer_out
    EndIf
    
    For ii = 0 To 31 : PokeA(@register(0)+ii, Val("$"+PeekS(@fixed_key_string$+iii, 2))) : iii+stepp : Next ; Create a key
    
    Repeat
      If Not AESEncoder(@register(0), @register(0), 32, @register(0), 256, 0, #PB_Cipher_ECB) : ProcedureReturn 0 : EndIf
      swap_=register(0) : register(0)=register(3) : register(3)=swap_ ; Never use here
      swap_=register(1) : register(1)=register(2) : register(2)=swap_ ; the PB Swap function !
      
      ; Dual crypting, you can activate, but you must not
      ; If Not AESEncoder(@register(0), @register(0), 16, @register(0), 256, 0, #PB_Cipher_ECB) : ProcedureReturn 0 : EndIf
      
      If mode=2
        Protected *register.word=@register(0)
        For ii=0 To 15 Step 2
          If *buffer_in\w And *buffer_in\w ! *register\w
            *buffer_out\w=*buffer_in\w ! *register\w
          Else
            *buffer_out\w=*buffer_in\w
          EndIf
          If i>bytes_minus_x : Break 2 : EndIf
          *buffer_in+2 : *buffer_out+2 : *register+2 : i+2
        Next ii
      ElseIf mode=1
        Protected *register_asc.ascii=@register(0)
        For ii=0 To 15
          If *buffer_in_asc\a And *buffer_in_asc\a ! *register_asc\a
            *buffer_out_asc\a=*buffer_in_asc\a ! *register_asc\a
          Else
            *buffer_out_asc\a=*buffer_in_asc\a
          EndIf
          If i>bytes_minus_x : Break 2 : EndIf
          *buffer_in_asc+1 : *buffer_out_asc+1 : *register_asc+1 : i+1
        Next ii
      Else
        *register_asc.ascii=@register(0)
        For ii=0 To 15
          *buffer_out_asc\a=*buffer_in_asc\a ! *register_asc\a
          If i>bytes_minus_x : Break 2 : EndIf
          *buffer_in_asc+1 : *buffer_out_asc+1 : *register_asc+1 : i+1
        Next ii
      EndIf
    ForEver
    
    ProcedureReturn 1
  EndProcedure
  
EndModule
UseModule QAES_smart_universal_coder

; =======================================================================

; Text to file crypter module
; Addon for the QAES_smart_universal_coder
DeclareModule QAES_TextToFileCrypter
  UseModule QAES_smart_universal_coder
  Declare create_encrypted_file(template_path_csv$, destination_path_csv$, key$)
  Declare read_encrypted_file(destination_path_csv$, List result.s(), key$)
EndDeclareModule

Module QAES_TextToFileCrypter
  EnableExplicit
  
  Procedure create_encrypted_file(template_path_csv$, destination_path_csv$, key$)
    Protected file=ReadFile(#PB_Any, template_path_csv$), *buffer, file_length.q=Lof(file), buffer_length.q
    If Not file : ProcedureReturn -1 : EndIf
    *buffer=AllocateMemory(file_length+16) : buffer_length=(MemorySize(*buffer))
    If Not *buffer : CloseFile(file) : ProcedureReturn -2 : EndIf
    If ReadData(file, *buffer, file_length)<>file_length
      CloseFile(file) : FreeMemory(*buffer) : ProcedureReturn -3
    EndIf
    CloseFile(file)
    file=CreateFile(#PB_Any, destination_path_csv$)
    If Not file : FreeMemory(*buffer) : ProcedureReturn -4 : EndIf
    If OpenCryptRandom() : CryptRandomData(*buffer+buffer_length-8, 8) : Else : RandomData(*buffer+buffer_length-8, 8) : EndIf
    If Not QAES_smart_universal_coder(0, *buffer, *buffer, buffer_length-8, key$, PeekQ(*buffer+buffer_length-8))
      CloseFile(file) : FreeMemory(*buffer) : ProcedureReturn -5
    EndIf
    If WriteData(file, *buffer, buffer_length)<>buffer_length
      CloseFile(file) : FreeMemory(*buffer) : ProcedureReturn -6
    EndIf
    CloseFile(file) : FreeMemory(*buffer)
    ProcedureReturn 1
  EndProcedure
  
  Procedure read_encrypted_file(destination_path_csv$, List result.s(), key$)
    Protected i, ii, line_length, peek_b, peek_w, row_break, counter.q, offset, *buffer, result$
    Protected file=ReadFile(#PB_Any, destination_path_csv$), file_length=Lof(file) , file_length_1=file_length-2
    If Not file : ProcedureReturn -7 : EndIf
    *buffer=AllocateMemory(file_length+8)
    If Not *buffer : ProcedureReturn -8 : EndIf
    FileSeek(file, file_length-8)
    counter=ReadQuad(file)
    FileSeek(file, 0)
    *buffer+2
    
    If ReadData(file, *buffer, file_length)<>file_length : CloseFile(file) : FreeMemory(*buffer) : EndIf
    CloseFile(file)
    If Not QAES_smart_universal_coder(0, *buffer, *buffer, file_length-8, key$, counter)
      FreeMemory(*buffer) : ProcedureReturn -9
    EndIf
    
    file_length-1
    For i=0 To file_length ; Search and handle row break
      peek_w=PeekW(*buffer+i) : peek_b=peek_w&$FF
      If peek_w=$0A0D : row_break=$0D : offset=2 : PokeW(*buffer-2, $0A0D) : Break : EndIf
      If peek_w=$0D0A : row_break=$0A : offset=2 : PokeW(*buffer-2, $0D0A) : Break : EndIf
      If peek_b=$0D : row_break=$0D : offset=1 : PokeB(*buffer-1, $0D) : Break : EndIf
      If peek_b=$0A : row_break=$0A : offset=1 : PokeB(*buffer-1, $0A) : Break : EndIf
    Next i
    
    If offset
      file_length-16-offset
      If offset<2 : ii-1 : Else :ii-2 : EndIf
      For i=ii To file_length
        If PeekB(*buffer+i)=row_break : i+offset
          line_length=*buffer+i+offset
          For ii=i To file_length : If PeekB(*buffer+ii)=row_break : Break : EndIf : Next
          AddElement(result())
          result()=PeekS(*buffer+i, *buffer+ii+offset-line_length, #PB_UTF8 | #PB_ByteLength)
        EndIf
      Next
    Else
      AddElement(result())
      result()=PeekS(*buffer, (*buffer+file_length-14)-*buffer, #PB_UTF8 | #PB_ByteLength)
    EndIf
    
    FreeMemory(*buffer-2)
    ProcedureReturn 1
  EndProcedure
  
EndModule
UseModule QAES_TextToFileCrypter

; ===============================================================================================================================================

; Hint : Dual crypting is deactivated in the QAES crypter

; For very large files you must deactivate the debugger

EnableExplicit

Define template_path_csv$=GetUserDirectory(#PB_Directory_Desktop)+"/QAES_Text_to_File_Crypter_UTF8/Test.csv"
Define destination_path_csv$=GetUserDirectory(#PB_Directory_Desktop)+"/QAES_Text_to_File_Crypter_UTF8/Test.csv [encrypted]"

Define key$= "This is a test key"

NewList result.s()

Debug "=== Encrypt and create a encrypted file ==="
create_encrypted_file(template_path_csv$, destination_path_csv$, key$) ; Create a encrypted file

Debug "=== Load the encrypted file and decrypt ==="
read_encrypted_file(destination_path_csv$, result.s(), key$) ; Read a encrypted file

Debug "=== Get the result ==="
Debug ""

ForEach result() ; Get the result
  Debug result()
Next
Last edited by walbus on Thu May 10, 2018 10:41 pm, edited 2 times in total.
User avatar
Andre
PureBasic Team
PureBasic Team
Posts: 2071
Joined: Fri Apr 25, 2003 6:14 pm
Location: Germany (Saxony, Deutscheinsiedel)
Contact:

Re: CSV special crypter QAES - Text>File - File>Text [Module

Post by Andre »

Hi Werner,

nice to see that you provide the module, which is already working very well for my project, now to the complete PureBasic community! Thank you :D
Bye,
...André
(PureBasicTeam::Docs & Support - PureArea.net | Order:: PureBasic | PureVisionXP)
walbus
Addict
Addict
Posts: 929
Joined: Sat Mar 02, 2013 9:17 am

Re: CSV special crypter QAES - Text>File - File>Text [Module

Post by walbus »

Hi Andre, yep, this is a very cool crypter.

Usually a crypter that works like this is not realizable.
I've never seen a crypter that can handle string termination. :)
Post Reply