Rabbit Cipher - Module

Applications, Games, Tools, User libs and useful stuff coded in PureBasic
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Rabbit Cipher - Module

Post by StarBootics »

wilbert wrote:I'll try to create a working asm version as well.
Some prefer the speed of asm, others the readability of normal basic code.
But it might help to compare the results of the two against each other.
I'm curious if your ASM version will be much faster. It's pretty fast as is already.

I have just uploaded the version 4. This version might be the last update about correcting issues.

See first post.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Rabbit Cipher - Module

Post by wilbert »

StarBootics wrote:I'm curious if your ASM version will be much faster. It's pretty fast as is already.
It will be a lot faster but it also depends on how you compile your code (32 or 64 bit).
For the asm version there will be little difference between 32 or 64 bit but with your code there's a big speed difference between the two.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Rabbit Cipher - Module

Post by StarBootics »

It will be a lot faster but it also depends on how you compile your code (32 or 64 bit).
For the asm version there will be little difference between 32 or 64 bit but with your code there's a big speed difference between the two.
In my code even if I have a 64-bit computer the Xor is being done on 64-bit instead of 32-bit, I have 32-bit of Xoring of complete waste. I know my code is just a serious hacking session and my knowledge of ASM is near zero so for the ASM version I can't help you.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Rabbit Cipher - Module

Post by wilbert »

Here's an asm optimized implementation you can play with ...

Module :

Code: Select all

;==================================
; Module:          Rabbit
; Author:          Wilbert
; Date:            Nov 12, 2020
;==================================

DeclareModule Rabbit
  
  ;-RabbitCtx structure
  Structure RabbitCtx
    X.l[8]  ; State
    C.l[8]  ; Counters
    Carry.l ; Carry for counters
  EndStructure
  
  ;-Exported functions
  Declare SetKeyAndIV(*Ctx.RabbitCtx, *Key, *IV)
  Declare ProcessBytes(*Ctx.RabbitCtx, *Input, *Output, Size)
  Declare CryptMemory(*Input, *Output, Size, *Key, *InitVector = #Null)
  
EndDeclareModule


Module Rabbit
  DisableDebugger
  EnableExplicit
  EnableASM
  
  ;- Private structures
  
  Structure Mem64Byte
    l.l[16]
  EndStructure
    
  ;- Private macros
  
  CompilerIf #PB_Compiler_Processor = #PB_Processor_x86
    Macro rbx : ebx : EndMacro  ; replace rbx with ebx on x86
    Macro rcx : ecx : EndMacro  ; replace rcx with ecx on x86
    Macro rdx : edx : EndMacro  ; replace rdx with edx on x86
    Macro rsi : esi : EndMacro  ; replace rsi with esi on x86
    Macro rdi : edi : EndMacro  ; replace rdi with edi on x86
    Macro rsp : esp : EndMacro  ; replace rsp with esp on x86
  CompilerEndIf  
  
  Macro M_AddWithCarry(n, v)
    mov eax, [rcx + n*4 + 32]   ; get c[n]
    adc eax, v                  ; add with carry v
    mov [rcx + n*4 + 32], eax   ; set c[n]
  EndMacro
  
  Macro M_GFunction(n)
    mov eax, [rcx + n*4]        ; get x[n]
    add eax, [rcx + n*4 + 32]   ; add c[n]
    mul eax                     ; eax*eax, result edx:eax
    XOr eax, edx                ; xor lower and upper result bits
    mov [rsp + n*4], eax        ; set g[n]
  EndMacro  
  
  Macro M_NewStateValue(n, n1, r1, n2, r2)
    mov eax, [rsp + n1*4]       ; get g[n1]
    mov edx, [rsp + n2*4]       ; get g[n2]
    rol eax, r1                 ; rol n1, r1
    CompilerIf r2
      rol edx, r2               ; rol n2, r2
    CompilerEndIf
    add eax, edx                ; add together
    add eax, [rsp + n*4]        ; add g[n]
    mov [rcx + n*4], eax        ; set x[n]
  EndMacro
  
  Macro M_SetStateAndCounter(k, n1, n2)
    CompilerIf k = 14           ; get key bits
      mov eax, [rdx] 
      shl eax, 16
      Or ax, [rdx + 14]
    CompilerElse
      mov eax, [rdx + k]
    CompilerEndIf
    mov [rcx + n1*4], eax       ; set x[n1]
    rol eax, 16
    mov [rcx + n2*4 + 32], eax  ; set c[n2]
  EndMacro
  
  Macro M_ModifyCounter(n1, n2)
    mov eax, [rcx + n2*4]       ; get x[n2]
    XOr [rcx + n1*4 + 32], eax  ; c[n1] ! x[n2]
  EndMacro
  
  Macro M_XorStreamBytes(n1, n2, n3, n4, f=0)
    mov eax, [rcx + n3*4]       ; get x[n3]
    mov edx, [rcx + n4*4]       ; get x[n4]
    shrd eax, edx, 16           ; (x[n3] >> 16) | (x[n4] << 16)
    XOr eax, [rcx + n2*4]       ; xor x[n2]
    CompilerIf f=0
      XOr eax, [rsi + n1*4]
      mov [rdi + n1*4], eax
    CompilerElse
      mov [rsp + n1*4], eax
    CompilerEndIf
  EndMacro
  
  ;- Public functions

  Procedure SetKeyAndIV(*Ctx.RabbitCtx, *Key, *IV)
    Protected M.Mem64Byte
    
    ; *Key : Pointer to 128 bit key
    ; *IV  : Pointer to 64 bit initialization vector
    
    mov rcx, [p.p_Ctx]
    mov rdx, [p.p_Key]
    
    ; Generate initial state and counter values
    M_SetStateAndCounter( 0, 0, 4)
    M_SetStateAndCounter( 2, 5, 1)
    M_SetStateAndCounter( 4, 2, 6)
    M_SetStateAndCounter( 6, 7, 3)
    M_SetStateAndCounter( 8, 4, 0)
    M_SetStateAndCounter(10, 1, 5)
    M_SetStateAndCounter(12, 6, 2)
    M_SetStateAndCounter(14, 3, 7)
    
    ; Reset carry flag
    mov dword [rcx + 64], 0
    
    ; Process 64 bytes
    ProcessBytes(*Ctx, @M, @M, 64)
    
    ; Modify counters
    mov rcx, [p.p_Ctx]
    M_ModifyCounter(0, 4)
    M_ModifyCounter(1, 5)
    M_ModifyCounter(2, 6)
    M_ModifyCounter(3, 7)
    M_ModifyCounter(4, 0)
    M_ModifyCounter(5, 1)
    M_ModifyCounter(6, 2)
    M_ModifyCounter(7, 3)

    If *IV
      ; Modify counter values
      mov rcx, [p.p_Ctx]
      mov rdx, [p.p_IV]
      mov eax, [rdx]
      mov edx, [rdx + 4]
      XOr [rcx + 32], eax
      XOr [rcx + 40], edx
      XOr [rcx + 48], eax
      XOr [rcx + 56], edx
      rol edx, 16
      xchg ax, dx
      rol eax, 16
      XOr [rcx + 36], eax
      XOr [rcx + 44], edx
      XOr [rcx + 52], eax
      XOr [rcx + 60], edx
      ; Process 64 bytes
      ProcessBytes(*Ctx, @M, @M, 64)
    EndIf
    
  EndProcedure
  
  Procedure ProcessBytes(*Ctx.RabbitCtx, *Input, *Output, Size)
    Protected.i reg_bx, reg_si, reg_di
    If Size
      mov [p.v_reg_bx], rbx
      mov [p.v_reg_si], rsi
      mov [p.v_reg_di], rdi
      mov rcx, [p.p_Ctx]    ; rcx = *Ctx
      mov rsi, [p.p_Input]  ; rsi = *Input
      mov rdi, [p.p_Output] ; rdi = *Output
      mov rbx, [p.v_Size]   ; rbx = Size
      sub rsp, 32           ; alloc g space
      !.loop16b:
      
      ; Calculate new counter values
      mov edx, [rcx + 64] ; load carry
      shr edx, 1
      M_AddWithCarry(0, 0x4d34d34d)
      M_AddWithCarry(1, 0xd34d34d3)
      M_AddWithCarry(2, 0x34d34d34)
      M_AddWithCarry(3, 0x4d34d34d)
      M_AddWithCarry(4, 0xd34d34d3)
      M_AddWithCarry(5, 0x34d34d34)
      M_AddWithCarry(6, 0x4d34d34d)
      M_AddWithCarry(7, 0xd34d34d3)
      adc edx, edx
      mov [rcx + 64], edx ; save carry
      
      ; Calculate the g-functions
      M_GFunction(0)
      M_GFunction(1)
      M_GFunction(2)
      M_GFunction(3)
      M_GFunction(4)
      M_GFunction(5)
      M_GFunction(6)
      M_GFunction(7)
      
      ; Calculate new state values
      M_NewStateValue(0, 7, 16, 6, 16)
      M_NewStateValue(1, 0,  8, 7,  0)
      M_NewStateValue(2, 1, 16, 0, 16)
      M_NewStateValue(3, 2,  8, 1,  0)
      M_NewStateValue(4, 3, 16, 2, 16)
      M_NewStateValue(5, 4,  8, 3,  0)
      M_NewStateValue(6, 5, 16, 4, 16)
      M_NewStateValue(7, 6,  8, 5,  0)
      
      ; Xor stream bytes
      sub rbx, 16
      !jc .final
      M_XorStreamBytes(0, 0, 5, 3)
      M_XorStreamBytes(1, 2, 7, 5)
      M_XorStreamBytes(2, 4, 1, 7)
      M_XorStreamBytes(3, 6, 3, 1)
      add rsi, 16
      add rdi, 16
      test rbx, rbx
      !jnz .loop16b
      !jmp .done
      !.final:
      M_XorStreamBytes(0, 0, 5, 3, 1)
      M_XorStreamBytes(1, 2, 7, 5, 1)
      M_XorStreamBytes(2, 4, 1, 7, 1)
      M_XorStreamBytes(3, 6, 3, 1, 1)
      add rbx, 15
      !.loop1b:
      movzx eax, byte [rsi + rbx]
      XOr al, [rsp + rbx]
      mov [rdi + rbx], al
      sub rbx, 1
      !jnc .loop1b
      !.done:
      
      add rsp, 32
      mov rbx, [p.v_reg_bx]
      mov rsi, [p.v_reg_si]
      mov rdi, [p.v_reg_di]
    EndIf
  EndProcedure
  
  
  Procedure CryptMemory(*Input, *Output, Size, *Key, *InitVector = #Null)
    ; Encrypt/decrypt a block of memory
    ; ---------------------------------    
    ; *Key : Pointer to 128 bit key
    ; *IV  : Pointer to 64 bit initialization vector    
    Protected Ctx.RabbitCtx
    SetKeyAndIV(@Ctx, *Key, *InitVector)
    ProcessBytes(@Ctx, *Input, *Output, Size)
  EndProcedure
  
EndModule


Test vectors :

Code: Select all

DataSection
  ; Test vectors
  
  Key1:
  Data.a $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
  Out1:
  Data.a $02,$F7,$4A,$1C,$26,$45,$6B,$F5,$EC,$D6,$A5,$36,$F0,$54,$57,$B1
  Data.a $A7,$8A,$C6,$89,$47,$6C,$69,$7B,$39,$0C,$9C,$C5,$15,$D8,$E8,$88
  Data.a $96,$D6,$73,$16,$88,$D1,$68,$DA,$51,$D4,$0C,$70,$C3,$A1,$16,$F4
  
  Key2:
  Data.a $C2,$1F,$CF,$38,$81,$CD,$5E,$E8,$62,$8A,$CC,$B0,$A9,$89,$0D,$F8
  Out2:
  Data.a $3D,$02,$E0,$C7,$30,$55,$91,$12,$B4,$73,$B7,$90,$DE,$E0,$18,$DF
  Data.a $CD,$6D,$73,$0C,$E5,$4E,$19,$F0,$C3,$5E,$C4,$79,$0E,$B6,$C7,$4A
  Data.a $B0,$BB,$1B,$B7,$86,$0A,$68,$5A,$BF,$9C,$8F,$AF,$26,$3C,$CA,$09

  Key3:
  Data.a $1D,$27,$2C,$6A,$2D,$8E,$3D,$FC,$AC,$14,$05,$6B,$78,$D6,$33,$A0
  Out3:
  Data.a $A3,$A9,$7A,$BB,$80,$39,$38,$20,$B7,$E5,$0C,$4A,$BB,$53,$82,$3D
  Data.a $C4,$42,$37,$99,$C2,$EF,$C9,$FF,$B3,$A4,$12,$5F,$1F,$4C,$99,$A8
  Data.a $AE,$95,$3E,$56,$D3,$8B,$D2,$67,$67,$C3,$64,$9E,$EF,$34,$D9,$19
  
  Key4:
  Data.a $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
  IV4:
  Data.a $00,$00,$00,$00,$00,$00,$00,$00
  Out4:
  Data.a $ED,$B7,$05,$67,$37,$5D,$CD,$7C,$D8,$95,$54,$F8,$5E,$27,$A7,$C6
  Data.a $8D,$4A,$DC,$70,$32,$29,$8F,$7B,$D4,$EF,$F5,$04,$AC,$A6,$29,$5F
  Data.a $66,$8F,$BF,$47,$8A,$DB,$2B,$E5,$1E,$6C,$DE,$29,$2B,$82,$DE,$2A  
  
  Key5:
  Data.a $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
  IV5:
  Data.a $59,$7E,$26,$C1,$75,$F5,$73,$C3
  Out5:
  Data.a $6D,$7D,$01,$22,$92,$CC,$DC,$E0,$E2,$12,$00,$58,$B9,$4E,$CD,$1F
  Data.a $2E,$6F,$93,$ED,$FF,$99,$24,$7B,$01,$25,$21,$D1,$10,$4E,$5F,$A7
  Data.a $A7,$9B,$02,$12,$D0,$BD,$56,$23,$39,$38,$E7,$93,$C3,$12,$C1,$EB  
  
  Key6:
  Data.a $00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
  IV6:
  Data.a $27,$17,$F4,$D2,$1A,$56,$EB,$A6
  Out6:
  Data.a $4D,$10,$51,$A1,$23,$AF,$B6,$70,$BF,$8D,$85,$05,$C8,$D8,$5A,$44
  Data.a $03,$5B,$C3,$AC,$C6,$67,$AE,$AE,$5B,$2C,$F4,$47,$79,$F2,$C8,$96
  Data.a $CB,$51,$15,$F0,$34,$F0,$3D,$31,$17,$1C,$A7,$5F,$89,$FC,$CB,$9F
  
EndDataSection


Procedure VerifyTestVector(*Key, *IV, *Out, VectorName.s)
  Protected Dim Stream.a(47)
  Protected Ctx.Rabbit::RabbitCtx
  Protected.i i, *a.Ascii = *Out
  
  Rabbit::SetKeyAndIV(@Ctx, *Key, *IV)
  Rabbit::ProcessBytes(@Ctx, @Stream(), @Stream(), 48)
  For i = 0 To 47
    If Stream(i) <> *a\a
      Debug VectorName + " [ERROR]"
      ProcedureReturn
    EndIf
    *a + 1
  Next
  Debug VectorName + " [ok]"
  
EndProcedure

VerifyTestVector(?Key1, #Null, ?Out1, "Vector 1")
VerifyTestVector(?Key2, #Null, ?Out2, "Vector 2")
VerifyTestVector(?Key3, #Null, ?Out3, "Vector 3")
VerifyTestVector(?Key4, ?IV4, ?Out4, "Vector 4")
VerifyTestVector(?Key5, ?IV5, ?Out5, "Vector 5")
VerifyTestVector(?Key6, ?IV6, ?Out6, "Vector 6")

Another small example

Code: Select all

DataSection
  MasterKey:
  Data.a $1D,$27,$2C,$6A,$2D,$8E,$3D,$FC,$AC,$14,$05,$6B,$78,$D6,$33,$A0
EndDataSection

Define.Rabbit::RabbitCtx Master, Work
Define.q TestValue, Encrypted, Decrypted

Rabbit::SetKeyAndIV(@Master, ?MasterKey, #Null)

TestValue = $1234567890abcdef

Work = Master
Rabbit::ProcessBytes(@Work, @TestValue, @Encrypted, 8)

Work = Master
Rabbit::ProcessBytes(@Work, @Encrypted, @Decrypted, 8)

Debug TestValue
Debug Encrypted
Debug Decrypted

Nov 8 update: Small improvement to the code of IVSsetup
Nov 11 update: Combined key and iv setup into one procedure
Nov 12 update: Integrated NextState into ProcessBytes for performance improvement
Last edited by wilbert on Fri Nov 13, 2020 3:10 pm, edited 3 times in total.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Rabbit Cipher - Module

Post by StarBootics »

Hello everyone,

@wilbert : Nice job with your ASM version !

As promised there is a convenient Read/Write on file OOP style library.

EDIT 1 : Minor correction related to Unsigned value. ReadU32()/WriteU32() added and RC_ReadString() / RC_WriteString() modified.
EDIT 2 : Minor modification for speed improvement about the Addition() procedure.

Best regards
StarBootics

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Object - V1.2.0
; Project name : Read Write RabbitCipher
; File name : Read Write RabbitCipher 128 - OOP.pb
; File Version : 1.0.2
; Programmation : OK
; Programmed by : StarBootics
; Creation Date : Noverber 8th, 2020
; Last update : November 8th, 2020
; Coded for PureBasic : V5.73 beta 2
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming notes
;
; 1. Rabbit was designed by Martin Boesgaard, Mette Vesterager, 
;    Thomas Pedersen, Jesper Christiansen and Ove Scavenius. 
;
;    https://www.ecrypt.eu.org/stream/rabbitpf.html
;
; 2. Type of Algorithm : Synchronous Stream Cipher 
;
; 3. Rabbit has been released into the public domain and may 
;    be used freely for any purpose.
; 
; 4. I deserve credit only for porting Rabbit Cipher from C to
;    PureBasic. I'm not the original designer of the algorithm
;    so no credit to me for that.
;
; 5. The software is provided "as is" without any express or 
;    implied warranty. You are using it at your own risk. The 
;    original authors and/or me shall not in any way be liable 
;    for any use of this software.
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule RabbitCipher

  Interface RabbitCipher
    
    GetKey.s()
    GetInitVector.s()
    SetKey(P_Key.s)
    SetInitVector(P_InitVector.s)
    
    RC_WriteString(FileID.i, P_String.s)
    RC_WriteByte(FileID.i, P_Value.b)
    RC_WriteAsciiCharacter(FileID.i, P_Value.a)
    RC_WriteWord(FileID.i, P_Value.w)
    RC_WriteUnicodeCharacter(FileID.i, P_Value.u)
    RC_WriteCharacter(FileID.i, P_Value.c)
    RC_WriteLong(FileID.i, P_Value.l)
    RC_WriteQuad(FileID.i, P_Value.q)
    RC_WriteInteger(FileID.i, P_Value.i)
    RC_WriteFloat(FileID.i, P_Value.f)
    RC_WriteDouble(FileID.i, P_Value.d)
    
    RC_ReadString.s(FileID.i)
    RC_ReadByte.b(FileID.i)
    RC_ReadAsciiCharacter.a(FileID.i)
    RC_ReadWord.w(FileID.i)
    RC_ReadUnicodeCharacter.u(FileID.i)
    RC_ReadCharacter.c(FileID.i)
    RC_ReadLong.l(FileID.i)
    RC_ReadQuad.q(FileID.i)
    RC_ReadInteger.i(FileID.i)
    RC_ReadFloat.f(FileID.i)
    RC_ReadDouble.d(FileID.i)
    
    Free()
    
  EndInterface
  
  Declare.i New(P_Key.s = "RabbitCipher", P_InitVector.s = "InitVector")
  
EndDeclareModule

Module RabbitCipher
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Structures declaration <<<<<

  Structure Private_Members
    
    VirtualTable.i
    Key.s
    InitVector.s
    
  EndStructure
  
  Structure Rabbit128
    
    X.q[8]
    C.q[8]
    Carry.q
    
  EndStructure

  Structure Temp128
    
    g.q[8]
    OldC.q[8]
    
  EndStructure

  Structure Buffer128
    
    Buffer.a[16]
    
  EndStructure

  Structure Core128
    
    Master.Rabbit128
    Work.Rabbit128
    
  EndStructure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Macros declaration <<<<<
  
  Macro U32V(v)
    
    ((v) & $FFFFFFFF)
    
  EndMacro
  
  Macro ROTL32(v, n)
    
    (U32V((v) << (n)) | ((v) >> (32 - (n))))
    
  EndMacro
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Procedures declaration (Private) <<<<<
  
  Procedure.s Normalizer(Input.s, BitsLength.l)
    
    Bytes.l = BitsLength >> 3
    Output.s = StringFingerprint(Input, #PB_Cipher_MD5)
    
    While StringByteLength(Output) < Bytes
      Output + StringFingerprint(Output, #PB_Cipher_MD5)
    Wend
    
    ProcedureReturn Left(Output, Bytes / SizeOf(Unicode))
  EndProcedure
  
  Procedure.q PeekU32(*Buffer)
    
    ; Even if a quad size is 64-bit, we are
    ; interested only by the first 32-bit.
    
    CopyMemory(*Buffer, @Var.q, 4)
    
    ProcedureReturn Var
  EndProcedure
  
  Procedure PokeU32(*Buffer, Var.q)
    
    ; Even if a quad size is 64-bit, we are
    ; interested only by the first 32-bit.
    
    CopyMemory(@Var, *Buffer, 4)
    
  EndProcedure

  Procedure.q ReadU32(FileID.i)

    ; Even if a quad size is 64-bit, we are
    ; interested only by the first 32-bit.

    ReadData(FileID, @Var.q, 4)
    
    ProcedureReturn Var
  EndProcedure
  
  Procedure WriteU32(FileID.i, Var.q)
    
    ; Even if a quad size is 64-bit, we are
    ; interested only by the first 32-bit.

    WriteData(FileID, @Var, 4)
    
  EndProcedure

  Procedure.q U8TO32_LITTLE(*p)
    
    B0.a = PeekA(*p+0)
    B1.a = PeekA(*p+1)
    B2.a = PeekA(*p+2)
    B3.a = PeekA(*p+3)
    
    ProcedureReturn U32V(B0 | B1 << 8 | B2 << 16 | B3 << 24)
  EndProcedure
  
  Procedure.q Addition(VarA.q, VarB.q, VarC.q = -1)
    
    Result.q = U32V(VarA + VarB)
    
    If VarC >= 0
      Result = U32V(Result + VarC)
    EndIf
    
    ProcedureReturn Result
  EndProcedure
  
  Procedure.q RABBIT_GFunc(x.q)
    
    x * x
    
    ProcedureReturn U32V(x ! (x >> 32))
  EndProcedure
  
  Procedure RABBIT_NextState128(*Rabbit.Rabbit128)
    
    ; Temporary variables
    Protected Temp.Temp128, Index.l
    
    ; Save old counter values
    For Index = 0 To 7 
      Temp\OldC[Index] = *Rabbit\C[Index]
    Next
    
    ; Calculate new counter values
    *Rabbit\C[0] = Addition(*Rabbit\C[0], $4D34D34D + *Rabbit\Carry)
    *Rabbit\C[1] = Addition(*Rabbit\C[1], $D34D34D3 + Bool(*Rabbit\C[0] < Temp\OldC[0]))
    *Rabbit\C[2] = Addition(*Rabbit\C[2], $34D34D34 + Bool(*Rabbit\C[1] < Temp\OldC[1]))
    *Rabbit\C[3] = Addition(*Rabbit\C[3], $4D34D34D + Bool(*Rabbit\C[2] < Temp\OldC[2]))
    
    *Rabbit\C[4] = Addition(*Rabbit\C[4], $D34D34D3 + Bool(*Rabbit\C[3] < Temp\OldC[3]))
    *Rabbit\C[5] = Addition(*Rabbit\C[5], $34D34D34 + Bool(*Rabbit\C[4] < Temp\OldC[4]))
    *Rabbit\C[6] = Addition(*Rabbit\C[6], $4D34D34D + Bool(*Rabbit\C[5] < Temp\OldC[5]))
    *Rabbit\C[7] = Addition(*Rabbit\C[7], $D34D34D3 + Bool(*Rabbit\C[6] < Temp\OldC[6]))
    
    *Rabbit\Carry = Bool(*Rabbit\C[7] < Temp\OldC[7])
    
    ; Calculate the g-values
    
    For Index = 0 To 7
      Temp\g[Index] = RABBIT_GFunc(Addition(*Rabbit\X[Index], *Rabbit\C[Index]))
    Next
    
    ; Calculate new state values
    
    *Rabbit\X[0] = Addition(Temp\g[0], ROTL32(Temp\g[7],16), ROTL32(Temp\g[6], 16))
    *Rabbit\X[1] = Addition(Temp\g[1], ROTL32(Temp\g[0], 8), Temp\g[7])
    *Rabbit\X[2] = Addition(Temp\g[2], ROTL32(Temp\g[1],16), ROTL32(Temp\g[0], 16))
    *Rabbit\X[3] = Addition(Temp\g[3], ROTL32(Temp\g[2], 8), Temp\g[1])
    *Rabbit\X[4] = Addition(Temp\g[4], ROTL32(Temp\g[3],16), ROTL32(Temp\g[2], 16))
    *Rabbit\X[5] = Addition(Temp\g[5], ROTL32(Temp\g[4], 8), Temp\g[3])
    *Rabbit\X[6] = Addition(Temp\g[6], ROTL32(Temp\g[5],16), ROTL32(Temp\g[4], 16))
    *Rabbit\X[7] = Addition(Temp\g[7], ROTL32(Temp\g[6], 8), Temp\g[5])
    
  EndProcedure

  Procedure RABBIT_KeySetup128(*Core.Core128, *Key)
    
    ; Temporary Variables
    Protected k0.q, k1.q, k2.q, k3.q, Index.l
    
    ; Generate four subkeys
    k0 = U8TO32_LITTLE(*Key + 0)
    k1 = U8TO32_LITTLE(*Key + 4)
    k2 = U8TO32_LITTLE(*Key + 8)
    k3 = U8TO32_LITTLE(*Key + 12)
    
    ; Generate initial state variables
    *Core\Master\X[0] = k0
    *Core\Master\X[2] = k1
    *Core\Master\X[4] = k2
    *Core\Master\X[6] = k3
    *Core\Master\X[1] = U32V(k3 << 16) | (k2 >> 16)
    *Core\Master\X[3] = U32V(k0 << 16) | (k3 >> 16)
    *Core\Master\X[5] = U32V(k1 << 16) | (k0 >> 16)
    *Core\Master\X[7] = U32V(k2 << 16) | (k1 >> 16)
    
    ; Generate initial counter values
    *Core\Master\C[0] = ROTL32(k2, 16)
    *Core\Master\C[2] = ROTL32(k3, 16)
    *Core\Master\C[4] = ROTL32(k0, 16)
    *Core\Master\C[6] = ROTL32(k1, 16)
    
    *Core\Master\C[1] = (k0 & $FFFF0000) | (k1 & $FFFF)
    *Core\Master\C[3] = (k1 & $FFFF0000) | (k2 & $FFFF)
    *Core\Master\C[5] = (k2 & $FFFF0000) | (k3 & $FFFF)
    *Core\Master\C[7] = (k3 & $FFFF0000) | (k0 & $FFFF)
    
    *Core\Master\Carry = 0
    
    ; Iterate the system four times
    
    For index = 0 To 3
      RABBIT_NextState128(*Core\Master)
    Next
    
    ; Modify the counters
    For Index = 0 To 7
      *Core\Master\C[Index] ! *Core\Master\X[(Index + 4) & 7]
    Next
    
    ; Copy master instance to work instance
    
    For Index = 0 To 7
      *Core\Work\X[Index] = *Core\Master\X[Index]
      *Core\Work\C[Index] = *Core\Master\C[Index]
    Next
    
    *Core\Work\Carry = *Core\Master\Carry
    
  EndProcedure
  
  Procedure RABBIT_InitVectorSetup128(*Core.Core128, *InitVector)
    
    ; Temporary variables
    Protected i0.q, i1.q, i2.q, i3.q, Index.l
    
    ; Generate four subvectors
    i0 = U8TO32_LITTLE(*InitVector + 0)
    i2 = U8TO32_LITTLE(*InitVector + 4)
    i1 = (i0 >> 16) | (i2 & $FFFF0000)
    i3 = (i2 << 16) | (i0 & $0000FFFF)
    
    ; Modify counter values
    
    *Core\Work\C[0] = *Core\Master\C[0] ! i0
    *Core\Work\C[1] = *Core\Master\C[1] ! i1
    *Core\Work\C[2] = *Core\Master\C[2] ! i2
    *Core\Work\C[3] = *Core\Master\C[3] ! i3
    
    *Core\Work\C[4] = *Core\Master\C[4] ! i0
    *Core\Work\C[5] = *Core\Master\C[5] ! i1
    *Core\Work\C[6] = *Core\Master\C[6] ! i2
    *Core\Work\C[7] = *Core\Master\C[7] ! i3
    
    ; Copy state variables
    
    For Index = 0 To 7
      *Core\Work\X[Index] = *Core\Master\X[Index]
    Next
    
    *Core\Work\Carry = *Core\Master\Carry
    
    ; Iterate the system four times
    
    For index = 0 To 3
      RABBIT_NextState128(*Core\Work)
    Next
    
  EndProcedure

  Procedure RABBIT_Process_Bytes128(*Core.Core128, *Input, *Output, MsgLen.l)
    
    ; Temporary variables 
    
    Protected Buffer.Buffer128, Index.l
    
    ; Encrypt/decrypt all full blocks
    
    While MsgLen >= 16
      
      ; Iterate the system
      RABBIT_NextState128(*Core\Work)
      
      ; Encrypt/decrypt 16 bytes of data 
      PokeU32(*Output + 00, PeekU32(*Input + 00) ! *Core\Work\X[0] ! (*Core\Work\X[5] >> 16) ! U32V(*Core\Work\X[3] << 16))
      PokeU32(*Output + 04, PeekU32(*Input + 04) ! *Core\Work\X[2] ! (*Core\Work\X[7] >> 16) ! U32V(*Core\Work\X[5] << 16))
      PokeU32(*Output + 08, PeekU32(*Input + 08) ! *Core\Work\X[4] ! (*Core\Work\X[1] >> 16) ! U32V(*Core\Work\X[7] << 16))
      PokeU32(*Output + 12, PeekU32(*Input + 12) ! *Core\Work\X[6] ! (*Core\Work\X[3] >> 16) ! U32V(*Core\Work\X[1] << 16))
      
      *Input + 16
      *Output + 16
      MsgLen - 16
      
    Wend
    
    ; Encrypt/decrypt remaining data
    
    If MsgLen <> 0
      
      ; Iterate the system
      RABBIT_NextState128(*Core\Work)
      
      ; Generate 16 bytes of pseudo-random data */
      
      PokeU32(@Buffer + 00, *Core\Work\X[0] ! (*Core\Work\X[5]>>16) ! U32V(*Core\Work\X[3]<<16))
      PokeU32(@Buffer + 04, *Core\Work\X[2] ! (*Core\Work\X[7]>>16) ! U32V(*Core\Work\X[5]<<16))
      PokeU32(@Buffer + 06, *Core\Work\X[4] ! (*Core\Work\X[1]>>16) ! U32V(*Core\Work\X[7]<<16))
      PokeU32(@Buffer + 12, *Core\Work\X[6] ! (*Core\Work\X[3]>>16) ! U32V(*Core\Work\X[1]<<16))
      
      For Index = 0 To MsgLen - 1
        PokeA(*Output, PeekA(*Input) ! Buffer\Buffer[Index])
        *Output + 1
        *Input + 1
      Next
      
    EndIf
    
  EndProcedure

  Procedure RABBIT_Encrypt(*This.Private_Members, *PlainText, *CipheredText, StringByteLength.l)
    
    NormKey.s = Normalizer(*This\Key, 128)
    RABBIT_KeySetup128(Core128.Core128, @NormKey)
    
    If *This\InitVector <> "NULL"
      NormInitVec.s = Normalizer(*This\InitVector, 64)
      RABBIT_InitVectorSetup128(Core128, @NormInitVec)
    EndIf
    
    RABBIT_Process_Bytes128(Core128, *PlainText, *CipheredText, StringByteLength)
    
  EndProcedure
  
  Procedure RABBIT_Decrypt(*This.Private_Members, *PlainText, *CipheredText, StringByteLength.l)
    
    NormKey.s = Normalizer(*This\Key, 128)
    RABBIT_KeySetup128(Core128.Core128, @NormKey)
    
    If *This\InitVector <> "NULL"
      NormInitVec.s = Normalizer(*This\InitVector, 64)
      RABBIT_InitVectorSetup128(Core128, @NormInitVec)
    EndIf
    
    RABBIT_Process_Bytes128(Core128, *CipheredText, *PlainText, StringByteLength)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Getters <<<<<

  Procedure.s GetKey(*This.Private_Members)
    
    ProcedureReturn *This\Key
  EndProcedure
  
  Procedure.s GetInitVector(*This.Private_Members)
    
    ProcedureReturn *This\InitVector
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Setters <<<<<

  Procedure SetKey(*This.Private_Members, P_Key.s)
    
    If P_Key <> ""
      *This\Key = P_Key
    Else
      *This\Key = "RabbitCipher"
    EndIf
    
  EndProcedure
  
  Procedure SetInitVector(*This.Private_Members, P_InitVector.s)
    
    If P_InitVector <> ""
      *This\InitVector = P_InitVector
    Else
      *This\InitVector = "InitVector"
    EndIf
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Write on file operators <<<<<
  
  Procedure RC_WriteString(*This.Private_Members, FileID.i, P_String.s)

    StringMemorySize.q = StringByteLength(P_String) + SizeOf(Character)
    WriteU32(FileID, StringMemorySize)
    
    *CipheredText = AllocateMemory(StringMemorySize)
    RABBIT_Encrypt(*This, @P_String, *CipheredText, StringByteLength(P_String))
    WriteData(FileID, *CipheredText, StringMemorySize)
    
    FreeMemory(*CipheredText)
    
  EndProcedure
  
  Procedure RC_WriteByte(*This.Private_Members, FileID.i, P_Value.b)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 6, "0"))
    
  EndProcedure
  
  Procedure RC_WriteAsciiCharacter(*This.Private_Members, FileID.i, P_Value.a)
    
    RC_WriteString(*This, FileID, RSet(Str(P_Value), 6, "0"))
    
  EndProcedure
  
  Procedure RC_WriteWord(*This.Private_Members, FileID.i, P_Value.w)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 10, "0"))
    
  EndProcedure
  
  Procedure RC_WriteUnicodeCharacter(*This.Private_Members, FileID.i, P_Value.u)
    
    RC_WriteString(*This, FileID, RSet(Str(P_Value), 10, "0"))
    
  EndProcedure
  
  Procedure RC_WriteCharacter(*This.Private_Members, FileID.i, P_Value.c)
    
    RC_WriteString(*This, FileID, RSet(Str(P_Value), 10, "0"))
    
  EndProcedure
  
  Procedure RC_WriteLong(*This.Private_Members, FileID.i, P_Value.l)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 22, "0"))
    
  EndProcedure
  
  Procedure RC_WriteQuad(*This.Private_Members, FileID.i, P_Value.q)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 42, "0"))
    
  EndProcedure
  
  Procedure RC_WriteInteger(*This.Private_Members, FileID.i, P_Value.i)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 42, "0"))
    
  EndProcedure
  
  Procedure RC_WriteFloat(*This.Private_Members, FileID.i, P_Value.f)
    
    RC_WriteString(*This, FileID, StrF(P_Value, 14))
    
  EndProcedure
  
  Procedure RC_WriteDouble(*This.Private_Members, FileID.i, P_Value.d)
    
    RC_WriteString(*This, FileID, StrD(P_Value, 25))
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Read on file operators <<<<<
  
  Procedure.s RC_ReadString(*This.Private_Members, FileID.i)

    StringMemorySize.q = ReadU32(FileID)
    *PlainText = AllocateMemory(StringMemorySize)
    *CipheredText = AllocateMemory(StringMemorySize)
    
    ReadData(FileID, *CipheredText, StringMemorySize)
    
    RABBIT_Decrypt(*This, *PlainText, *CipheredText, StringMemorySize - SizeOf(Character))
    DecryptedString.s = PeekS(*PlainText)
    
    FreeMemory(*PlainText)
    FreeMemory(*CipheredText)
    
    ProcedureReturn DecryptedString
  EndProcedure
  
  Procedure.b RC_ReadByte(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.a RC_ReadAsciiCharacter(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.w RC_ReadWord(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.u RC_ReadUnicodeCharacter(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.c RC_ReadCharacter(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.l RC_ReadLong(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.q RC_ReadQuad(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.i RC_ReadInteger(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.f RC_ReadFloat(*This.Private_Members, FileID.i)
    
    ProcedureReturn ValF(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.d RC_ReadDouble(*This.Private_Members, FileID.i)
    
    ProcedureReturn ValD(RC_ReadString(*This, FileID))
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Destructor <<<<<

  Procedure Free(*This.Private_Members)
    
    FreeStructure(*This)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Constructor <<<<<

  Procedure.i New(P_Key.s = "RabbitCipher", P_InitVector.s = "InitVector")
    
    *This.Private_Members = AllocateStructure(Private_Members)
    *This\VirtualTable = ?START_METHODS
    
    SetKey(*This, P_Key)
    SetInitVector(*This, P_InitVector)
    
    ProcedureReturn *This
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Virtual Table Entries <<<<<

  DataSection
    START_METHODS:
    Data.i @GetKey()
    Data.i @GetInitVector()
    Data.i @SetKey()
    Data.i @SetInitVector()
    Data.i @RC_WriteString()
    Data.i @RC_WriteByte()
    Data.i @RC_WriteAsciiCharacter()
    Data.i @RC_WriteWord()
    Data.i @RC_WriteUnicodeCharacter()
    Data.i @RC_WriteCharacter()
    Data.i @RC_WriteLong()
    Data.i @RC_WriteQuad()
    Data.i @RC_WriteInteger()
    Data.i @RC_WriteFloat()
    Data.i @RC_WriteDouble()
    Data.i @RC_ReadString()
    Data.i @RC_ReadByte()
    Data.i @RC_ReadAsciiCharacter()
    Data.i @RC_ReadWord()
    Data.i @RC_ReadUnicodeCharacter()
    Data.i @RC_ReadCharacter()
    Data.i @RC_ReadLong()
    Data.i @RC_ReadQuad()
    Data.i @RC_ReadInteger()
    Data.i @RC_ReadFloat()
    Data.i @RC_ReadDouble()
    Data.i @Free()
    END_METHODS:
  EndDataSection
  
EndModule

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.001 seconds (96000.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

CompilerIf #PB_Compiler_IsMainFile
  
  UseMD5Fingerprint()
  RC.RabbitCipher::RabbitCipher = RabbitCipher::New()
  
  Varw.w = 32700
  Varl.l = -2147483645
  Varq.q = 9223372036854775800
  Varf.f = 2 * #PI
  Vard.d = 3 * #PI
  Text.s = "J'aime les déesses nordiques super sexy !"
  
  Debug "; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
  Debug "; Test on file"
  Debug ""
  Debug "Original : "
  
  Debug Varw
  Debug Varl
  Debug Varq
  Debug Varf
  Debug Vard
  Debug Text
  Debug ""
  
  If CreateFile(0, "Test.rc")
    
    RC\SetKey("PureBasic 5.72") ; Set the key
    RC\SetInitVector("") ; Restore the default Init vector
    
    RC\RC_WriteWord(0, Varw) 
    RC\RC_WriteLong(0, Varl)  
    RC\RC_WriteQuad(0, Varq)
    
    RC\SetKey("") ; Restore the default key
    RC\SetInitVector("NULL") ; No Init vector here
    
    RC\RC_WriteFloat(0, Varf)
    RC\RC_WriteDouble(0, Vard)
    RC\RC_WriteString(0, Text)
    
    CloseFile(0)
    
  EndIf 
  
  Debug "FileSize : " + Str(FileSize("Test.rc")) 
  Debug "From the file : " 
  
  If ReadFile(1, "Test.rc")
    
    RC\SetKey("PureBasic 5.72") ; Set the key
    RC\SetInitVector("") ; Restore the default Init vector
    
    Debug RC\RC_ReadWord(1)
    Debug RC\RC_ReadLong(1)
    Debug RC\RC_ReadQuad(1)
    
    RC\SetKey("") ; Restore the default key
    RC\SetInitVector("NULL")  ; No Init vector here
    
    Debug RC\RC_ReadFloat(1)
    Debug RC\RC_ReadDouble(1)
    Debug RC\RC_ReadString(1)
    
    CloseFile(1)
    DeleteFile("Test.rc")
    
  EndIf 
  
  RC\Free()
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
The Stone Age did not end due to a shortage of stones !
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: Rabbit Cipher - Module

Post by Saki »

Hi, you might want to explain the way it works a bit more, otherwise hardly any of the people will use it.
It is also important to explain what happens if you encrypt twice with the same IV and key, otherwise it is a trap.
And how to create the key and IV correctly.
And how to encrypt very large files in blocks with progress.
What to do with the IV and why it is extremely important in this type of crypters to ensure the integrity of a decrypted file.

Primarily this is a very good thing and a good replacement for PB AES, a light version that is extremely easy to use.

Best Regards Saki
地球上の平和
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Rabbit Cipher - Module

Post by StarBootics »

Saki wrote:Hi, you might want to explain the way it works a bit more, otherwise hardly any of the people will use it.
I'm still searching ways to improve my hacking sessions for improvement.
Saki wrote:It is also important to explain what happens if you encrypt twice with the same IV and key, otherwise it is a trap.
I'm not sure about that.
Saki wrote:And how to create the key and IV correctly.
I 'm still working on that aspect. The current way of generating Key and IV for those of you have their alphabet coded between character 32 to 255, the key and init vector are created like this :

000 Value 000 value 000 value 000 ...

it's not an invalid Key or Init Vector they are part of their respective spaces 2^128 keys possible and 2^64 Init Vectors possible but I don't like that. If someone has a better solution for the "Normalizer()" procedure I will be glad to integrate it. But some of you will probably prefer to put Keys and Init Vectors created with a Crypto random generator into DataSection inside their programs. So at this point it's a preference and a design choice. Use your imagination to modify my code to fit your needs.
Saki wrote:And how to encrypt very large files in blocks with progress.
To do that you need to work with thread, count how many block with the given file size, and a counter inside the Process_Byte procedure and compare the two. So the design has to be reviewed for that.
Saki wrote:What to do with the IV and why it is extremely important in this type of crypters to ensure the integrity of a decrypted file.
I'm not an expert in Cryptographic algorithm at all. It's probably best to read this document : https://www.ecrypt.eu.org/stream/p3ciph ... bit_p3.pdf
Saki wrote:Primarily this is a very good thing and a good replacement for PB AES, a light version that is extremely easy to use.
What I don't like about AES is the fact that short strings can't be ciphered without padding them. It's a real pain.

To make a long story short, some work has to be done and it will require more time and research to solve the remaining issues.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Rabbit Cipher - Module

Post by wilbert »

StarBootics wrote:If someone has a better solution for the "Normalizer()" procedure I will be glad to integrate it.
When I searched on the internet for password hashing, algorithms like PBKDF2 or Argon2 came up.
For PBKDF2 there already seems to be a PureBasic module on the German forum.
I don't have much knowledge about this myself. Personally I might even use something like a FNV hash but that probably isn't very secure.
Saki wrote:And how to encrypt very large files in blocks with progress.
I tried and a buffer of 128KiB seems to work very good.
Tracking progress could be done with a callback procedure or posting events.
What seems difficult to me is for example if you allow the input file to be overwritten or not. :?
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: Rabbit Cipher - Module

Post by Saki »

Hi,
some text about it.

It is sufficient to specify a source and a destination address, and to specify the bytes to be encrypted.

A File or String Crypter should be created as a separate module.

The IV is usually added before or after the data.

Also, the Rabbit Algorytmus is really well suited to support the AES implementation of PB with a
easy to handle alternative to be added.
I am not aware that it was broken.
Primarily attacks on the password are the weak point.

There is nothing to be said against generating a hash from a SHA3 hash by iteration,
whereby a salt can be processed as well.

The established procedures for key generation are supposed to slow down the key calculation
and complicate the implementation in hardware.

With the advancing development of computer technology, these procedures are increasingly
ineffektive.

A SHA3 Hash with an appropriate salt, or a pass phrase and 1e5 iterations you are quickly beyond all imaginable efforts.
Always finalize the iterations of key generation and checksum generating.

With the blockwise transfer you can, after defining the procedure, as well as with the key generation
can also be proceeded differently.
If you set the protocol you can also set the first 8 bytes of the first encrypted block
as IV for the second block to be encrypted, etc.
This corresponds to the CBC mode.

Yes, the input file is usually overwritten directly.

It always depends on what you want.
If the writing process breaks off for some reason when you overwrite directly, it is not good.
To encrypt disks but always like this.

Everything that is temporarily unencrypted is a problem.
But with SSD this is a bit different.
A new write operation uses a new block.
If no Trim is executed in the meantime, the data remains as a shadow.
This is all very complex.

The read and write accesses are fastest if you do not go below 16384 (4096*4) bytes for the blocks, but only for mechanical disks.
More than 16384 does almost nothing.
The progress is shown with a timer, about 30 > 100 ms, then it does not brake, faster 100 looks better.

An encrypted file must always have a checksum, in fact a crypt hash.
Otherwise small errors are not noticed or the files can be manipulated unnoticed.
Because reading and writing is the same, other problems can occur.
Encrypted data can be marked with a Crypt marker.
Primarily a row of zeros or spaces at the end is enough, because something like this can not occur in an encrypted file.
The encrypted files may only consist of random data without any recognizable header or extender.

Best Regards Saki
地球上の平和
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3870
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: Rabbit Cipher - Module

Post by wilbert »

Thanks for the explanation Saki.
It makes me realize that there's more to it then I thought which I have little or no knowledge about.
So I'll leave my asm based Rabbit module posted in this thread as it is; capable of processing a block of memory like you mentioned.
I did do a speed comparison with the PureBasic AESEncoder command.
On my computer the Rabbit cipher was about 4 times faster for a large block of memory but I don't know if PB makes use of hardware AES support modern processors offer.
Windows (x64)
Raspberry Pi OS (Arm64)
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: Rabbit Cipher - Module

Post by Saki »

Hi Wilbert,
PB use only Software AES.
Your Module is perfect, i like it !

Best Regards Saki
地球上の平和
User avatar
Saki
Addict
Addict
Posts: 830
Joined: Sun Apr 05, 2020 11:28 am
Location: Pandora

Re: Rabbit Cipher - Module

Post by Saki »

File crypter with automatically randomized IV, progressbar and blockwise CBC crypting added.

Damn fast :twisted:

viewtopic.php?f=27&t=76286

Best Regards Saki
地球上の平和
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Rabbit Cipher - Module

Post by StarBootics »

Hello everyone,

After a deep review I have finally came up with a better solution for the Normalizer() procedure. Also I have discovered a problem when counter system was reset at every information written in the file. According to the documentation the Rabbit cipher was design to cipher 2^64 blocks of 16 bytes each without any reset. So let's do some math here :

2^64 -> 18 446 744 073 709 551 616 blocks of 16 bytes each -> 295 147 905 179 352 825 856 bytes -> 1 099 511 627 776 Tera-bytes

Now the setup will be done by the constructor automatically and it has to be done manually when the Key or the InitVector or both changes. I have also optimize the Addition() procedure to be much faster. Finally I have added a RC_WriteData() and RC_ReadData() methods to the mix so now you can cipher data if needed (see the example).

So without any further due, this is the version 2.0.0 of the convenient Read/Write on file library.

Code: Select all

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; AUTOMATICALLY GENERATED CODE, DO NOT MODIFY
; UNLESS YOU REALLY, REALLY, REALLY MEAN IT !!
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Code generated by : Dev-Object - V1.2.0
; Project name : Read Write RabbitCipher
; File name : Read Write RabbitCipher 128 - OOP.pb
; File Version : 2.0.0
; Programmation : OK
; Programmed by : StarBootics
; Creation Date : Noverber 8th, 2020
; Last update : November 19th, 2020
; Coded for PureBasic : V5.73 beta 4
; Platform : Windows, Linux, MacOS X
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Programming notes
;
; 1. Rabbit was designed by Martin Boesgaard, Mette Vesterager, 
;    Thomas Pedersen, Jesper Christiansen and Ove Scavenius. 
;
;    https://www.ecrypt.eu.org/stream/rabbitpf.html
;
; 2. Type of Algorithm : Synchronous Stream Cipher 
;
; 3. Rabbit has been released into the public domain and may 
;    be used freely for any purpose.
; 
; 4. I deserve credit only for porting Rabbit Cipher from C to
;    PureBasic. I'm not the original designer of the algorithm
;    so no credit to me for that.
;
; 5. The software is provided "as is" without any express or 
;    implied warranty. You are using it at your own risk. The 
;    original authors and/or me shall not in any way be liable 
;    for any use of this software.
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

DeclareModule RabbitCipher

  Interface RabbitCipher
    
    GetKey.s()
    GetInitVector.s()
    SetKey(P_Key.s)
    SetInitVector(P_InitVector.s)
    RunSetup()
    
    RC_WriteString(FileID.i, P_String.s)
    RC_WriteByte(FileID.i, P_Value.b)
    RC_WriteAsciiCharacter(FileID.i, P_Value.a)
    RC_WriteWord(FileID.i, P_Value.w)
    RC_WriteUnicodeCharacter(FileID.i, P_Value.u)
    RC_WriteCharacter(FileID.i, P_Value.c)
    RC_WriteLong(FileID.i, P_Value.l)
    RC_WriteQuad(FileID.i, P_Value.q)
    RC_WriteInteger(FileID.i, P_Value.i)
    RC_WriteFloat(FileID.i, P_Value.f)
    RC_WriteDouble(FileID.i, P_Value.d)
    RC_WriteData(FileID.i, *Memory, Size.l) 
    
    RC_ReadString.s(FileID.i)
    RC_ReadByte.b(FileID.i)
    RC_ReadAsciiCharacter.a(FileID.i)
    RC_ReadWord.w(FileID.i)
    RC_ReadUnicodeCharacter.u(FileID.i)
    RC_ReadCharacter.c(FileID.i)
    RC_ReadLong.l(FileID.i)
    RC_ReadQuad.q(FileID.i)
    RC_ReadInteger.i(FileID.i)
    RC_ReadFloat.f(FileID.i)
    RC_ReadDouble.d(FileID.i)
    RC_ReadData(FileID.i, *Memory, Size.l)
    
    Free()
    
  EndInterface
  
  Declare.i New(P_Key.s = "RabbitCipher", P_InitVector.s = "InitVector")
  
EndDeclareModule

Module RabbitCipher
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Structures declaration <<<<<
  
  Structure Rabbit
    
    X.q[8]
    C.q[8]
    Carry.q
    
  EndStructure
  
  Structure Temp
    
    g.q[8]
    OldC.q[8]
    
  EndStructure
  
  Structure Buffer
    
    Buffer.a[16]
    
  EndStructure
  
  Structure Core
    
    Master.Rabbit
    Work.Rabbit
    
  EndStructure
  
  Structure Private_Members
    
    VirtualTable.i
    Key.s
    InitVector.s
    IsSetupDone.b
    Core.Core
    
  EndStructure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Macros declaration <<<<<
  
  Macro U32V(v)
    
    ((v) & $FFFFFFFF)
    
  EndMacro
  
  Macro ROTL32(v, n)
    
    (U32V((v) << (n)) | ((v) >> (32 - (n))))
    
  EndMacro
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< Procedures declaration (Private) <<<<<
  
  Procedure.i Normalizer(Input.s, BitsLength.l)
    
    Byte = BitsLength >> 3
    
    Output.s = StringFingerprint(Input, #PB_Cipher_MD5)
    
    *Normalized = AllocateMemory(Byte)
    *Cursor = *Normalized
    
    For Index = 1 To Byte
      
      PokeA(*Cursor, Val("$" + Mid(Output, Index, 2)))
      *Cursor + 1
      
    Next
    
    ProcedureReturn *Normalized
  EndProcedure
  
  Procedure.q PeekU32(*Buffer)
    
    ; Even if a quad size is 64-bit, we are
    ; interested only by the first 32-bit.
    
    CopyMemory(*Buffer, @Var.q, 4)
    
    ProcedureReturn Var
  EndProcedure
  
  Procedure PokeU32(*Buffer, Var.q)
    
    ; Even if a quad size is 64-bit, we are
    ; interested only by the first 32-bit.
    
    CopyMemory(@Var, *Buffer, 4)
    
  EndProcedure
  
  Procedure.q ReadU32(FileID.i)
    
    ReadData(FileID, @Var.q, 4)
    
    ProcedureReturn Var
  EndProcedure
  
  Procedure WriteU32(FileID.i, Var.q)
    
    WriteData(FileID, @Var, 4)
    
  EndProcedure
  
  Procedure.q U8TO32_LITTLE(*p)
    
    B0.a = PeekA(*p+0)
    B1.a = PeekA(*p+1)
    B2.a = PeekA(*p+2)
    B3.a = PeekA(*p+3)
    
    ProcedureReturn U32V(B0 | B1 << 8 | B2 << 16 | B3 << 24)
  EndProcedure
  
  Procedure.q Addition(VarA.q, VarB.q, VarC.q = 0)
    
    Result.q = U32V(VarA + VarB)
    Result = U32V(Result + VarC)
    
    ProcedureReturn Result
  EndProcedure
  
  Procedure.q RABBIT_GFunc(x.q)
    
    x * x
    
    ProcedureReturn U32V(x ! (x >> 32))
  EndProcedure
  
  Procedure RABBIT_NextState128(*Rabbit.Rabbit)
    
    ; Temporary variables
    Protected Temp.Temp, Index.l
    
    ; Save old counter values
    For Index = 0 To 7 
      Temp\OldC[Index] = *Rabbit\C[Index]
    Next
    
    ; Calculate new counter values
    *Rabbit\C[0] = Addition(*Rabbit\C[0], $4D34D34D + *Rabbit\Carry)
    *Rabbit\C[1] = Addition(*Rabbit\C[1], $D34D34D3 + Bool(*Rabbit\C[0] < Temp\OldC[0]))
    *Rabbit\C[2] = Addition(*Rabbit\C[2], $34D34D34 + Bool(*Rabbit\C[1] < Temp\OldC[1]))
    *Rabbit\C[3] = Addition(*Rabbit\C[3], $4D34D34D + Bool(*Rabbit\C[2] < Temp\OldC[2]))
    
    *Rabbit\C[4] = Addition(*Rabbit\C[4], $D34D34D3 + Bool(*Rabbit\C[3] < Temp\OldC[3]))
    *Rabbit\C[5] = Addition(*Rabbit\C[5], $34D34D34 + Bool(*Rabbit\C[4] < Temp\OldC[4]))
    *Rabbit\C[6] = Addition(*Rabbit\C[6], $4D34D34D + Bool(*Rabbit\C[5] < Temp\OldC[5]))
    *Rabbit\C[7] = Addition(*Rabbit\C[7], $D34D34D3 + Bool(*Rabbit\C[6] < Temp\OldC[6]))
    
    *Rabbit\Carry = Bool(*Rabbit\C[7] < Temp\OldC[7])
    
    ; Calculate the g-values
    
    For Index = 0 To 7
      Temp\g[Index] = RABBIT_GFunc(Addition(*Rabbit\X[Index], *Rabbit\C[Index]))
    Next
    
    ; Calculate new state values
    
    *Rabbit\X[0] = Addition(Temp\g[0], ROTL32(Temp\g[7],16), ROTL32(Temp\g[6], 16))
    *Rabbit\X[1] = Addition(Temp\g[1], ROTL32(Temp\g[0], 8), Temp\g[7])
    *Rabbit\X[2] = Addition(Temp\g[2], ROTL32(Temp\g[1],16), ROTL32(Temp\g[0], 16))
    *Rabbit\X[3] = Addition(Temp\g[3], ROTL32(Temp\g[2], 8), Temp\g[1])
    *Rabbit\X[4] = Addition(Temp\g[4], ROTL32(Temp\g[3],16), ROTL32(Temp\g[2], 16))
    *Rabbit\X[5] = Addition(Temp\g[5], ROTL32(Temp\g[4], 8), Temp\g[3])
    *Rabbit\X[6] = Addition(Temp\g[6], ROTL32(Temp\g[5],16), ROTL32(Temp\g[4], 16))
    *Rabbit\X[7] = Addition(Temp\g[7], ROTL32(Temp\g[6], 8), Temp\g[5])
    
  EndProcedure

  Procedure RABBIT_KeySetup128(*Core.Core, *Key)
    
    ; Temporary Variables
    Protected k0.q, k1.q, k2.q, k3.q, Index.l
    
    ; Generate four subkeys
    k0 = U8TO32_LITTLE(*Key + 0)
    k1 = U8TO32_LITTLE(*Key + 4)
    k2 = U8TO32_LITTLE(*Key + 8)
    k3 = U8TO32_LITTLE(*Key + 12)
    
    ; Generate initial state variables
    *Core\Master\X[0] = k0
    *Core\Master\X[2] = k1
    *Core\Master\X[4] = k2
    *Core\Master\X[6] = k3
    *Core\Master\X[1] = U32V(k3 << 16) | (k2 >> 16)
    *Core\Master\X[3] = U32V(k0 << 16) | (k3 >> 16)
    *Core\Master\X[5] = U32V(k1 << 16) | (k0 >> 16)
    *Core\Master\X[7] = U32V(k2 << 16) | (k1 >> 16)
    
    ; Generate initial counter values
    *Core\Master\C[0] = ROTL32(k2, 16)
    *Core\Master\C[2] = ROTL32(k3, 16)
    *Core\Master\C[4] = ROTL32(k0, 16)
    *Core\Master\C[6] = ROTL32(k1, 16)
    
    *Core\Master\C[1] = (k0 & $FFFF0000) | (k1 & $FFFF)
    *Core\Master\C[3] = (k1 & $FFFF0000) | (k2 & $FFFF)
    *Core\Master\C[5] = (k2 & $FFFF0000) | (k3 & $FFFF)
    *Core\Master\C[7] = (k3 & $FFFF0000) | (k0 & $FFFF)
    
    *Core\Master\Carry = 0
    
    ; Iterate the system four times
    
    For index = 0 To 3
      RABBIT_NextState128(*Core\Master)
    Next
    
    ; Modify the counters
    For Index = 0 To 7
      *Core\Master\C[Index] ! *Core\Master\X[(Index + 4) & 7]
    Next
    
    ; Copy master instance to work instance
    
    For Index = 0 To 7
      *Core\Work\X[Index] = *Core\Master\X[Index]
      *Core\Work\C[Index] = *Core\Master\C[Index]
    Next
    
    *Core\Work\Carry = *Core\Master\Carry
    
  EndProcedure
  
  Procedure RABBIT_InitVectorSetup128(*Core.Core, *InitVector)
    
    ; Temporary variables
    Protected i0.q, i1.q, i2.q, i3.q, Index.l
    
    ; Generate four subvectors
    i0 = U8TO32_LITTLE(*InitVector + 0)
    i2 = U8TO32_LITTLE(*InitVector + 4)
    i1 = (i0 >> 16) | (i2 & $FFFF0000)
    i3 = (i2 << 16) | (i0 & $0000FFFF)
    
    ; Modify counter values
    
    *Core\Work\C[0] = *Core\Master\C[0] ! i0
    *Core\Work\C[1] = *Core\Master\C[1] ! i1
    *Core\Work\C[2] = *Core\Master\C[2] ! i2
    *Core\Work\C[3] = *Core\Master\C[3] ! i3
    
    *Core\Work\C[4] = *Core\Master\C[4] ! i0
    *Core\Work\C[5] = *Core\Master\C[5] ! i1
    *Core\Work\C[6] = *Core\Master\C[6] ! i2
    *Core\Work\C[7] = *Core\Master\C[7] ! i3
    
    ; Copy state variables
    
    For Index = 0 To 7
      *Core\Work\X[Index] = *Core\Master\X[Index]
    Next
    
    *Core\Work\Carry = *Core\Master\Carry
    
    ; Iterate the system four times
    
    For index = 0 To 3
      RABBIT_NextState128(*Core\Work)
    Next
    
  EndProcedure

  Procedure RABBIT_Process_Bytes128(*Core.Core, *Input, *Output, MsgLen.l)
    
    ; Temporary variables 
    
    Protected Buffer.Buffer, Index.l
    
    ; Encrypt/decrypt all full blocks
    
    While MsgLen >= 16
      
      ; Iterate the system
      RABBIT_NextState128(*Core\Work)
      
      ; Encrypt/decrypt 16 bytes of data 
      PokeU32(*Output + 00, PeekU32(*Input + 00) ! *Core\Work\X[0] ! (*Core\Work\X[5] >> 16) ! U32V(*Core\Work\X[3] << 16))
      PokeU32(*Output + 04, PeekU32(*Input + 04) ! *Core\Work\X[2] ! (*Core\Work\X[7] >> 16) ! U32V(*Core\Work\X[5] << 16))
      PokeU32(*Output + 08, PeekU32(*Input + 08) ! *Core\Work\X[4] ! (*Core\Work\X[1] >> 16) ! U32V(*Core\Work\X[7] << 16))
      PokeU32(*Output + 12, PeekU32(*Input + 12) ! *Core\Work\X[6] ! (*Core\Work\X[3] >> 16) ! U32V(*Core\Work\X[1] << 16))
      
      *Input + 16
      *Output + 16
      MsgLen - 16
      
    Wend
    
    ; Encrypt/decrypt remaining data
    
    If MsgLen <> 0
      
      ; Iterate the system
      RABBIT_NextState128(*Core\Work)
      
      ; Generate 16 bytes of pseudo-random data */
      
      PokeU32(@Buffer + 00, *Core\Work\X[0] ! (*Core\Work\X[5]>>16) ! U32V(*Core\Work\X[3]<<16))
      PokeU32(@Buffer + 04, *Core\Work\X[2] ! (*Core\Work\X[7]>>16) ! U32V(*Core\Work\X[5]<<16))
      PokeU32(@Buffer + 06, *Core\Work\X[4] ! (*Core\Work\X[1]>>16) ! U32V(*Core\Work\X[7]<<16))
      PokeU32(@Buffer + 12, *Core\Work\X[6] ! (*Core\Work\X[3]>>16) ! U32V(*Core\Work\X[1]<<16))
      
      For Index = 0 To MsgLen - 1
        PokeA(*Output, PeekA(*Input) ! Buffer\Buffer[Index])
        *Output + 1
        *Input + 1
      Next
      
    EndIf
    
  EndProcedure

  Procedure RABBIT_Encrypt(*This.Private_Members, *PlainText, *CipheredText, StringByteLength.l)
    
    RABBIT_Process_Bytes128(*This\Core, *PlainText, *CipheredText, StringByteLength)
    
  EndProcedure

  Procedure RABBIT_Decrypt(*This.Private_Members, *PlainText, *CipheredText, StringByteLength.l)
    
    RABBIT_Process_Bytes128(*This\Core, *CipheredText, *PlainText, StringByteLength)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Getters <<<<<

  Procedure.s GetKey(*This.Private_Members)
    
    ProcedureReturn *This\Key
  EndProcedure
  
  Procedure.s GetInitVector(*This.Private_Members)
    
    ProcedureReturn *This\InitVector
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Setters <<<<<

  Procedure SetKey(*This.Private_Members, P_Key.s)
    
    If P_Key <> ""
      *This\Key = P_Key
    Else
      *This\Key = "RabbitCipher"
    EndIf
    
    *This\IsSetupDone = #False
    
  EndProcedure
  
  Procedure SetInitVector(*This.Private_Members, P_InitVector.s)
    
    If P_InitVector <> ""
      *This\InitVector = P_InitVector
    Else
      *This\InitVector = "InitVector"
    EndIf
    
    *This\IsSetupDone = #False
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The RunSetup operator <<<<<
  
  Procedure RunSetup(*This.Private_Members)
    
    NormKey = Normalizer(*This\Key, 128)
    RABBIT_KeySetup128(*This\Core, NormKey)
    FreeMemory(NormKey)
    
    If *This\InitVector <> "NULL"
      NormInitVec = Normalizer(*This\InitVector, 64)
      RABBIT_InitVectorSetup128(*This\Core, NormInitVec)
      FreeMemory(NormInitVec)
    EndIf
    
    *This\IsSetupDone = #True
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Write on file operators <<<<<
  
  Procedure RC_WriteString(*This.Private_Members, FileID.i, P_String.s)
    
    If *This\IsSetupDone = #True
      
      StringMemorySize.q = StringByteLength(P_String) + SizeOf(Character)
      WriteU32(FileID, StringMemorySize)
      
      *CipheredText = AllocateMemory(StringMemorySize)
      RABBIT_Encrypt(*This, @P_String, *CipheredText, StringByteLength(P_String))
      WriteData(FileID, *CipheredText, StringMemorySize)
      
      FreeMemory(*CipheredText)
      
    EndIf 
    
  EndProcedure
  
  Procedure RC_WriteByte(*This.Private_Members, FileID.i, P_Value.b)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 6, "0"))
    
  EndProcedure
  
  Procedure RC_WriteAsciiCharacter(*This.Private_Members, FileID.i, P_Value.a)
    
    RC_WriteString(*This, FileID, RSet(Str(P_Value), 6, "0"))
    
  EndProcedure
  
  Procedure RC_WriteWord(*This.Private_Members, FileID.i, P_Value.w)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 10, "0"))
    
  EndProcedure
  
  Procedure RC_WriteUnicodeCharacter(*This.Private_Members, FileID.i, P_Value.u)
    
    RC_WriteString(*This, FileID, RSet(Str(P_Value), 10, "0"))
    
  EndProcedure
  
  Procedure RC_WriteCharacter(*This.Private_Members, FileID.i, P_Value.c)
    
    RC_WriteString(*This, FileID, RSet(Str(P_Value), 10, "0"))
    
  EndProcedure
  
  Procedure RC_WriteLong(*This.Private_Members, FileID.i, P_Value.l)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 22, "0"))
    
  EndProcedure
  
  Procedure RC_WriteQuad(*This.Private_Members, FileID.i, P_Value.q)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 42, "0"))
    
  EndProcedure
  
  Procedure RC_WriteInteger(*This.Private_Members, FileID.i, P_Value.i)
    
    If P_Value < 0
      Sign.s = "-"
      P_Value = P_Value * -1
    EndIf
    
    RC_WriteString(*This, FileID, Sign + RSet(Str(P_Value), 42, "0"))
    
  EndProcedure
  
  Procedure RC_WriteFloat(*This.Private_Members, FileID.i, P_Value.f)
    
    RC_WriteString(*This, FileID, StrF(P_Value, 14))
    
  EndProcedure
  
  Procedure RC_WriteDouble(*This.Private_Members, FileID.i, P_Value.d)
    
    RC_WriteString(*This, FileID, StrD(P_Value, 25))
    
  EndProcedure
  
  Procedure RC_WriteData(*This.Private_Members, FileID.i, *Memory, Size.l) 
    
    If *This\IsSetupDone = #True
      
      If *Memory <> #Null
        
        *CipheredMemory = AllocateMemory(Size)
        RABBIT_Encrypt(*This, *Memory, *CipheredMemory, Size)
        WriteData(FileID, *CipheredMemory, Size)
        FreeMemory(*CipheredMemory)
        
      EndIf
      
    EndIf
    
  EndProcedure

  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Read on file operators <<<<<
  
  Procedure.s RC_ReadString(*This.Private_Members, FileID.i)
    
    If *This\IsSetupDone = #True
      
      StringMemorySize.q = ReadU32(FileID)
      *PlainText = AllocateMemory(StringMemorySize)
      *CipheredText = AllocateMemory(StringMemorySize)
      
      ReadData(FileID, *CipheredText, StringMemorySize)
      
      RABBIT_Decrypt(*This, *PlainText, *CipheredText, StringMemorySize - SizeOf(Character))
      DecryptedString.s = PeekS(*PlainText)
      
      FreeMemory(*PlainText)
      FreeMemory(*CipheredText)
      
    EndIf
    
    ProcedureReturn DecryptedString
  EndProcedure
  
  Procedure.b RC_ReadByte(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.a RC_ReadAsciiCharacter(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.w RC_ReadWord(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.u RC_ReadUnicodeCharacter(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.c RC_ReadCharacter(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.l RC_ReadLong(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.q RC_ReadQuad(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.i RC_ReadInteger(*This.Private_Members, FileID.i)
    
    ProcedureReturn Val(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.f RC_ReadFloat(*This.Private_Members, FileID.i)
    
    ProcedureReturn ValF(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure.d RC_ReadDouble(*This.Private_Members, FileID.i)
    
    ProcedureReturn ValD(RC_ReadString(*This, FileID))
  EndProcedure
  
  Procedure RC_ReadData(*This.Private_Members, FileID.i, *Memory, Size.l)
    
    If *This\IsSetupDone = #True
      
      If *Memory <> #Null
        
        *CipheredMemory = AllocateMemory(Size)
        bytes = ReadData(FileID, *CipheredMemory, Size)
        RABBIT_Decrypt(*This, *Memory, *CipheredMemory, Size)
        FreeMemory(*CipheredMemory)
        
      EndIf
      
    EndIf
    
    ProcedureReturn bytes
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Destructor <<<<<

  Procedure Free(*This.Private_Members)
    
    FreeStructure(*This)
    
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Constructor <<<<<

  Procedure.i New(P_Key.s = "RabbitCipher", P_InitVector.s = "InitVector")
    
    *This.Private_Members = AllocateStructure(Private_Members)
    *This\VirtualTable = ?START_METHODS
    
    SetKey(*This, P_Key)
    SetInitVector(*This, P_InitVector)
    RunSetup(*This)
    
    ProcedureReturn *This
  EndProcedure
  
  ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  ; <<<<< The Virtual Table Entries <<<<<

  DataSection
    START_METHODS:
    Data.i @GetKey()
    Data.i @GetInitVector()
    Data.i @SetKey()
    Data.i @SetInitVector()
    Data.i @RunSetup()
    
    Data.i @RC_WriteString()
    Data.i @RC_WriteByte()
    Data.i @RC_WriteAsciiCharacter()
    Data.i @RC_WriteWord()
    Data.i @RC_WriteUnicodeCharacter()
    Data.i @RC_WriteCharacter()
    Data.i @RC_WriteLong()
    Data.i @RC_WriteQuad()
    Data.i @RC_WriteInteger()
    Data.i @RC_WriteFloat()
    Data.i @RC_WriteDouble()
    Data.i @RC_WriteData()
    
    Data.i @RC_ReadString()
    Data.i @RC_ReadByte()
    Data.i @RC_ReadAsciiCharacter()
    Data.i @RC_ReadWord()
    Data.i @RC_ReadUnicodeCharacter()
    Data.i @RC_ReadCharacter()
    Data.i @RC_ReadLong()
    Data.i @RC_ReadQuad()
    Data.i @RC_ReadInteger()
    Data.i @RC_ReadFloat()
    Data.i @RC_ReadDouble()
    Data.i @RC_ReadData()
    
    Data.i @Free()
    END_METHODS:
  EndDataSection
  
EndModule

; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Code generated in : 00.001 seconds (96000.00 lines/second) <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

CompilerIf #PB_Compiler_IsMainFile
  
  UseMD5Fingerprint()
  RC.RabbitCipher::RabbitCipher = RabbitCipher::New()
  
  Varw.w = 32700
  Varl.l = -2147483645
  Varq.q = 9223372036854775800
  Varf.f = 2 * #PI
  Vard.d = 3 * #PI
  Text.s = "J'aime les déesses nordiques super sexy !"
  
  Buffer.i = AllocateMemory(10 * SizeOf(Long))
  FillMemory(Buffer, MemorySize(Buffer), -255, #PB_Long)
  
  Debug "; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
  Debug "; Test on file"
  Debug ""
  Debug "Original : "
  
  Debug Varw
  Debug Varl
  Debug Varq
  Debug Varf
  Debug Vard
  Debug Text
  Debug ""
  
  If CreateFile(0, "Test.rc")
    
    RC\SetKey("PureBasic 5.72") ; Set the key
    RC\SetInitVector("") ; Restore the default Init vector
    RC\RunSetup()
    
    RC\RC_WriteWord(0, Varw) 
    RC\RC_WriteLong(0, Varl)  
    RC\RC_WriteQuad(0, Varq)
    
    RC\SetKey("") ; Restore the default key
    RC\SetInitVector("NULL") ; No Init vector here
    RC\RunSetup()
    
    RC\RC_WriteFloat(0, Varf)
    RC\RC_WriteDouble(0, Vard)
    RC\RC_WriteString(0, Text)
    
    RC\RC_WriteLong(0, MemorySize(Buffer))
    RC\RC_WriteData(0, Buffer, MemorySize(Buffer))
    
    CloseFile(0)
    
  EndIf 
  
  Debug "FileSize : " + Str(FileSize("Test.rc")) 
  Debug "From the file : " 
  
  If ReadFile(1, "Test.rc")
    
    RC\SetKey("PureBasic 5.72") ; Set the key
    RC\SetInitVector("") ; Restore the default Init vector
    RC\RunSetup()
    
    Debug RC\RC_ReadWord(1)
    Debug RC\RC_ReadLong(1)
    Debug RC\RC_ReadQuad(1)
    
    RC\SetKey("") ; Restore the default key
    RC\SetInitVector("NULL")  ; No Init vector here
    RC\RunSetup()
    
    Debug RC\RC_ReadFloat(1)
    Debug RC\RC_ReadDouble(1)
    Debug RC\RC_ReadString(1)
    
    MemorySize.l = RC\RC_Readlong(1)
    
    Buffer2 = AllocateMemory(MemorySize)
    RC\RC_ReadData(1, Buffer2, MemorySize)
    
    Debug "Compare Buffer vs Buffer2 (0 = KO, 1 = OK) -> " + Str(CompareMemory(Buffer, Buffer2, MemorySize))
    
    CloseFile(1)
    DeleteFile("Test.rc")
    
  EndIf 
  
  RC\Free()
  
CompilerEndIf

; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<
The Stone Age did not end due to a shortage of stones !
loulou2522
Enthusiast
Enthusiast
Posts: 496
Joined: Tue Oct 14, 2014 12:09 pm

Re: Rabbit Cipher - Module

Post by loulou2522 »

HI STarBootics,
When a false Key is given whn decrypting a file , the programm doesn't do anything , no crash no advertising. Was itpossible to test if the pass encrypt is false and in this case making an advertisment and close the programm.
Thanks in advance
User avatar
StarBootics
Addict
Addict
Posts: 984
Joined: Sun Jul 07, 2013 11:35 am
Location: Canada

Re: Rabbit Cipher - Module

Post by StarBootics »

loulou2522 wrote:HI STarBootics,
When a false Key is given whn decrypting a file , the programm doesn't do anything , no crash no advertising. Was itpossible to test if the pass encrypt is false and in this case making an advertisment and close the programm.
Thanks in advance
Yes it's possible, the example I provided was not design with that in mind. The tricks here is to write a specific string in the file when you create it. Then when you read the file you use the password to read the specific string from the file and compare it with the specific string if they are the same then you read the rest of the file if not then you can pop a MessageRequester() telling the user that the password is wrong.

The code snippet should look like this :

Code: Select all

#SpecificString = "SpecificString"

Procedure CreateMySecureFile(FileName.s, Password.s)
  
  If CreateFile(0, FileName)
    
    CustomLib.RabbitCipher::RabbitCipher = RabbitCipher::New(Password, "InitVector")
    
    CustomLib\RC_WriteString(0, #SpecificString)
    
    ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
    ; Write the rest of the information you need
    
    CloseFile(0)
    
  EndIf
  
EndProcedure 

Procedure ReadMySecureFile(FileName.s, Password.s)
  
  If ReadFile(0, FileName)
    
    CustomLib.RabbitCipher::RabbitCipher = RabbitCipher::New(Password, "InitVector")
    
    If CustomLib\RC_ReadString(0) = #SpecificString
      
      ; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
      ; Read the rest of the information you need
      
    Else
      
      MessageRequester("BAD PASSWORD", "The password provided is wrong !")
      
    EndIf
    
    CloseFile(0)
    
  EndIf
  
EndProcedure
I hope the provided snippet will help you to figure out what you want to do.

Best regards
StarBootics
The Stone Age did not end due to a shortage of stones !
Post Reply