I know some of you are not fans of own implementation of cipher algorithm. So the following code is not for you then, so leave it.
For the others this is something fun to play with.
In the example I'm using the MD5 StringFingerprint() for the key and the InitVector and this is not recommended at all especially for the InitVector.
Maybe creating a CryptRandom() sets of values put into a DataSection will be better for security reasons.
As long as the key is 128 bits and the InitVector is 64 bits in length almost anything is possible to do.
EDIT 1 : This is the 3rd version of the example and this time the code is really safe to use and conform to the RFC 4503 Rabbit Encryption of May 2006 or at least conform to the Appendix B of the document. Sorry for the inconvenience of me posting a not so verified source code at the beginning. The example at the end of the code show how to store keys and Init Vectors into Data Section.
EDIT 2 : Code updated to version 4 due to some issue caused by the PeekL()/PokeL() plus other issues pointed out by Wilbert
EDIT 3 : Code updated to version 4.0.1 for Speed improvement with the Addition() procedure.
THE CODE IS NOW CONSIDERED SAFE TO USE
Best regards
StarBootics
Code: Select all
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; Project Name : Rabbit Cipher
; File Name : RabbitCipher - Module.pb
; File Version : 4.0.1
; Programmation : OK
; Programmed by : StarBootics
; Creation Date : October 30th, 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.
;
; 6. Is this encryption system compatible with the RFC 4503
; Rabbit Encryption of May 2006 ?
;
; The Answer is YES !
;
; 7. Special thanks to Wilbert for the simplification of the
; G function procedure and other issues.
;
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
DeclareModule RabbitCipher
Declare.s Normalizer(Input.s, BitsLength.l)
Declare Encrypt(*Key, *InitVector, *PlainText, *CipheredText, StringByteLength.l)
Declare Decrypt(*Key, *InitVector, *PlainText, *CipheredText, StringByteLength.l)
EndDeclareModule
Module RabbitCipher
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Structures declaration <<<<<
Structure Rabbit
X.q[8]
C.q[8]
Carry.q
EndStructure
Structure Rabbit_Temp
g.q[8]
OldC.q[8]
EndStructure
Structure Buffer
Buffer.a[16]
EndStructure
Structure Core
Master.Rabbit
Work.Rabbit
EndStructure
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Macros declaration <<<<<
Macro U32V(v)
((v) & $FFFFFFFF)
EndMacro
Macro ROTL32(v, n)
((U32V((v) << (n)) | ((v) >> (32 - (n)))))
EndMacro
Macro SWAP32(v)
((ROTL32((v), 8) & $00FF00FF) | (ROTL32((v), 24) & $FF00FF00))
EndMacro
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Procedures declaration (Private) <<<<<
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 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 RABBIT_GFunc(x.q)
x * x
ProcedureReturn U32V(x ! (x >> 32))
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 RABBIT_NextState(*Rabbit.Rabbit)
; Temporary variables
Protected Temp.Rabbit_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
For Index = 0 To 7
If Index & 1 = 0
*Rabbit\X[Index] = Addition(Temp\g[Index], ROTL32(Temp\g[(Index + 7) & 7],16), ROTL32(Temp\g[(Index + 6) & 7], 16))
Else
*Rabbit\X[Index] = Addition(Temp\g[Index], ROTL32(Temp\g[(Index + 7) & 7], 8), Temp\g[(Index + 6) & 7])
EndIf
Next
EndProcedure
Procedure RABBIT_KeySetup(*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
For Index = 0 To 3
RABBIT_NextState(*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_InitVectorSetup(*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_NextState(*Core\Work)
Next
EndProcedure
Procedure RABBIT_Process_Bytes(*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_NextState(*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_NextState(*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
; <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
; <<<<< Procedures declaration (Public) <<<<<
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 Encrypt(*Key, *InitVector, *PlainText, *CipheredText, StringByteLength.l)
RABBIT_KeySetup(Core.Core, *Key)
If *InitVector <> #Null
RABBIT_InitVectorSetup(Core, *InitVector)
EndIf
RABBIT_Process_Bytes(Core, *PlainText, *CipheredText, StringByteLength)
EndProcedure
Procedure Decrypt(*Key, *InitVector, *PlainText, *CipheredText, StringByteLength.l)
RABBIT_KeySetup(Core.Core, *Key)
If *InitVector <> #Null
RABBIT_InitVectorSetup(Core, *InitVector)
EndIf
RABBIT_Process_Bytes(Core, *CipheredText, *PlainText, StringByteLength)
EndProcedure
EndModule
CompilerIf #PB_Compiler_IsMainFile
UseMD5Fingerprint()
String.s = "Hello, this is a test for Rabbit Cipher !"
Key.s = RabbitCipher::Normalizer("I'm the key !", 128)
InitVector.s = RabbitCipher::Normalizer("I'm the InitVector !", 64)
StringMemorySize = StringByteLength(String) + SizeOf(Character)
*CipheredText = AllocateMemory(StringMemorySize)
*CipheredText2 = AllocateMemory(StringMemorySize)
*PlainText = AllocateMemory(StringMemorySize)
*PlainText2 = AllocateMemory(StringMemorySize)
RabbitCipher::Encrypt(@Key, @InitVector, @String, *CipheredText, StringByteLength(String))
RabbitCipher::Encrypt(@Key, #Null, @String, *CipheredText2, StringByteLength(String))
RabbitCipher::Decrypt(@Key, @InitVector, *PlainText, *CipheredText, StringByteLength(String))
RabbitCipher::Decrypt(@Key, #Null, *PlainText2, *CipheredText2, StringByteLength(String))
PlainText.s = PeekS(*PlainText)
PlainText2.s = PeekS(*PlainText2)
Debug "Test Real life : Key setup, IV setup and encryption/decryption"
If String = PlainText
Debug "Success"
Else
Debug "Failure"
EndIf
Debug "Test Real life : Key setup and encryption/decryption"
If String = PlainText2
Debug "Success"
Else
Debug "Failure"
EndIf
FreeMemory(*CipheredText)
FreeMemory(*CipheredText2)
FreeMemory(*PlainText)
FreeMemory(*PlainText2)
CompilerEndIf
; <<<<<<<<<<<<<<<<<<<<<<<
; <<<<< END OF FILE <<<<<
; <<<<<<<<<<<<<<<<<<<<<<<