Using the PureBasic AESEncoder/AESDecoder in CBC mode in a correct way is not too easy.
(Topic is inspired by "walbus", see http://www.purebasic.fr/english/viewtop ... =7&t=68776 )
@walbus: Thanks, your AES OFB Cryptor helped understanding the situation.
A textual password (unicode) should be converted to binary hash with 32 Bytes for full complexity 256^32.
The InitializationVector with 16 Bytes should be changed for every new string/file.
The InitializationVector should be included in the ciphered data to be transfered to the recipient.
Data below 16 Bytes should be padded to 16 Bytes for AES-CBC encoding.
Here is my fully working demo code to start the criticism (save in PB 5.61 as UTF8 file format):
Code: Select all
;Using PureBasic 5.61 AESEncoder/AESDecoder in CBC mode for strings, with a textual password
;AES CBC 256 Bit Encode/Decode (Unicode ready)
Key$ = "The answer is 42 (Σ)." ;Textual password
String$ = "This is a test for AES CBC. The quick brown fox jumps over the lazy dog (Σ)." ;Data to be crypted
;String$ = "abcΣ" ;test for less than 16 Bytes
;Create input buffer from unicode string (or do analogous for file input)
OriginalByteSize.q = StringByteLength(String$)
If OriginalByteSize < 16 ;minimum 16 bytes for AES CBC encoding
*UncipheredData = AllocateMemory(16)
FillMemory(*UncipheredData, 16, $20, #PB_Character) ;padding with blanks
PokeS(*UncipheredData, String$, -1, #PB_String_NoZero)
Else
*UncipheredData = AllocateMemory(OriginalByteSize)
PokeS(*UncipheredData, String$, -1, #PB_String_NoZero)
EndIf
;ShowMemoryViewer(*UncipheredData, MemorySize(*UncipheredData))
;Create InitializationVector with 16 Bytes (to be changed for every new string/file)
*InitVect = AllocateMemory(16)
If OpenCryptRandom() And *InitVect
CryptRandomData(*InitVect, 16)
CloseCryptRandom()
Else
Debug "ERROR: Cryptography API not available"
End
EndIf
;ShowMemoryViewer(*InitVect, MemorySize(*InitVect))
;Create CryptoKey with 32 Bytes (Unicode ready)
;Binary Hash with complexity 256^32 ( http://www.purebasic.fr/english/viewtopic.php?f=7&t=68776 )
UseSHA3Fingerprint()
KeySalted$ = Key$ + "dS8g%68D5_fg8df,5gR6:5dFg+8d5f7;6g57d6f?sD675)f7s6" ; add some salt
KeyHash$ = Fingerprint(@KeySalted$, StringByteLength(KeySalted$), #PB_Cipher_SHA3, 256)
Debug KeyHash$
;Create 32 Bytes (values) from Unicode-Hex-String
*KeyHash = AllocateMemory(32)
For i = 0 To 31 : PokeA(*KeyHash+i, Val("$"+PeekS(@KeyHash$+4*i,2,#PB_Unicode))) : Next
;ShowMemoryViewer(*KeyHash, MemorySize(*KeyHash))
;Encode AES in CBC mode
*CipheredData = AllocateMemory(MemorySize(*UncipheredData))
If Not AESEncoder(*UncipheredData, *CipheredData, MemorySize(*UncipheredData), *KeyHash, 256, *InitVect, #PB_Cipher_CBC)
Debug "ERROR: AESEncoder"
End
EndIf
;ShowMemoryViewer(*CipheredData, MemorySize(*CipheredData))
;Save InitVector and size of original data to the beginning of crypted data
If CreateFile(0, "Crypted.aes")
WriteQuad(0, OriginalByteSize) ; Size of original data
WriteData(0, *InitVect, 16) ; InitVector
WriteData(0, *CipheredData, MemorySize(*CipheredData)) ; Crypted data
CloseFile(0)
Else
Debug "ERROR: WriteFile"
End
EndIf
;;;;;;;;;;; Alice --> Bob ;;;;;;;;;;;;;
;Reread crypted data
If ReadFile(0, "Crypted.aes")
OriginalByteSize2.q = ReadQuad(0) ; Size of original data
*InitVect2 = AllocateMemory(16)
ReadData(0, *InitVect2, 16) ; Original InitVector
DataSize2.q = Lof(0)-8-16
*CipheredData2 = AllocateMemory(DataSize2)
ReadData(0, *CipheredData2, DataSize2) ; Crypted data
CloseFile(0)
Else
Debug "ERROR: ReadFile"
End
EndIf
;Debug OriginalByteSize2
;ShowMemoryViewer(*InitVect2, MemorySize(*InitVect2))
;ShowMemoryViewer(*CipheredData2, MemorySize(*CipheredData2))
;Decode AES in CBC mode
*DecipheredData2 = AllocateMemory(MemorySize(*CipheredData2))
If Not AESDecoder(*CipheredData2, *DecipheredData2, MemorySize(*CipheredData2), *KeyHash, 256, *InitVect2, #PB_Cipher_CBC)
Debug "ERROR: AESDecoder"
End
EndIf
Debug PeekS(*DecipheredData2, OriginalByteSize2/SizeOf(Character), #PB_Unicode)
;Don't forget to release all allocated memory