Encryption across all Xojo platforms, including iOS

I have used Einhugur (AES CBC) to encrypt sensitive fields in my databases on desktop, console and web for years. Now I want to create an iOS version of my app and decrypt those fields.
Here is my dilemma:

  1. MBS Encryption Kit works, but supports only iOS and macOS ie no Windows or Linux
  2. Einhugur and M_Crypto support only desktop, console and web ie no iOS

So I need a multi-provider solution ie MBS Encryption Kit and one other. So I wrote a simple piece of code to encrypt a sample string with a password key and an IV Key, then HexEncode it, assuming they would all return the same Hex. Boy was I wrong! Every system produces different Hex results. Given they are all using public libraries behind the scenes, either my code is wrong or I am missing something. The Hex code is in the comment line after each encryption.

As MBS Encryption Kit is the only game in town for iOS encryption (that I know of), I need a Windows, Linux encryption solution that can encrypt/decrypt the text in the same way as the MBS Encryption Kit. I was hoping M_Crypto was this solution.

Can someone either tell me what I am doing wrong or inform me of another cross-platform solution that includes iOS?

[code]Const kInputText As String = “Some Text to encrypt…” '> 0 bytes and less than 4 GBytes
Dim kMBInputText As Xojo.Core.MemoryBlock = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(kInputText.ToText)

Const kPassword As String = “12345678901234567890123456789012” '32 bytes
Dim kPasswordText As Text = kPassword.ToText
Dim mbPasswordHash As Xojo.Core.MemoryBlock = CommonCryptoMB.Hash(CommonCryptoMB.Hashes.SHA256, kPasswordText)
Dim mbPassword As Xojo.Core.MemoryBlock = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(kPassword.ToText)

Const kIVKey As String = “1234567890123456” '16 bytes
Dim mbIVKey As Xojo.Core.MemoryBlock = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(kIVKey.ToText)
Dim mbIVKeyEmpty As Xojo.Core.MemoryBlock

Dim returnText As Text
Dim returnString As String

'MBS Encryption Kit for macOS and iOS only
Dim Encryptor As New CommonCryptorMB(CommonCryptoMB.CryptoOperation.Encrypt, CommonCryptoMB.CryptoMode.kCCModeCBC, CommonCryptoMB.CryptoAlgorithm.AES, CommonCryptoMB.CryptoPadding.PKCS7, kPasswordText, mbIVKey)
Dim enData1 As Xojo.Core.MemoryBlock = Encryptor.Update(kMBInputText)
Dim enData2 As Xojo.Core.MemoryBlock = Encryptor.Final1
Dim EncryptedData As New Xojo.Core.MutableMemoryBlock(enData1)
EncryptedData.Append(enData2)
returnText = CommonCryptoMB.EncodeHex(encryptedData)
// 1B25F04DF6A1B0091AFDA65BFE2963BCB98E44277D21C06E8E19FD9A9AE6A1EE

'M_Crypto for macOS, Windows and Linux on desktop, console and web only
Dim bf As Blowfish_MTC
bf = New Blowfish_MTC(kPassword, Blowfish_MTC.Padding.PKCS)
bf.SetInitialVector kIVKey
returnString = EncodeHex(bf.EncryptCBC(kInputText))
// A641C0421611BB9B14753C62D6D181A5DAADAFB240A3FDCF

'M_Crypto for macOS, Windows and Linux on desktop, console and web only
Dim tempAES_MTC As AES_MTC
tempAES_MTC = New AES_MTC(kPasswordText, AES_MTC.EncryptionBits.Bits192, AES_MTC.Padding.PKCS)
tempAES_MTC.SetInitialVector(kIVKey)
returnString = EncodeHex(tempAES_MTC.EncryptCBC(kInputText))
// E006D62B6DB2734C2BB2105DC7B949471C2F8C771F1D979DBBEF6D987A883CA0 as Bits128
// 2163D24D8B14F0CE64AE87CB391E7BA96B07C88003C2851B247FAA7AD8F43C78 as Bits192
// 0B8B533D0C6F6A016A9FCB97C82B875513AFFA8640F966BFBD09A028AE0199ED as Bits256

'Einhugur for macOS, Windows and Linux on desktop, console and web only
Dim tempAES_CBC As AES_CBC
tempAES_CBC = New AES_CBC(kPassword, kIVKey)
returnString = EncodeHex(tempAES_CBC.Encrypt(kInputText) + tempAES_CBC.FinishEncrypt)
// 0B8B533D0C6F6A016A9FCB97C82B875572797074E280A6 'same as M_Crypto 256 bit for first 32 Hex bytes

'Einhugur for macOS, Windows and Linux on desktop, console and web only
Dim twoFish As New TwofishCBC(kPassword, 0, kIVKey) '0=Encrypt, 1=Decrypt, 2=Don’t know
returnString = EncodeHex(twoFish.Encrypt(kInputText) + twoFish.FinishEncrypt)
// E50E715116917951B659508FABD6F8CC72797074E280A6[/code]

Check the length of your password / secret.
If the length does not match what the encryption algorithm requires, the plugin / module will either be truncating it or padding it to the required size. I imagine different suppliers will use a different approach for this.

did you try MBS Encryption Plugin For Mac, Windows and Linux?

[quote=470587:@Kevin Gale]Check the length of your password / secret.
If the length does not match what the encryption algorithm requires, the plugin / module will either be truncating it or padding it to the required size. I imagine different suppliers will use a different approach for this.[/quote]
And… the Data you’re going to De/Encrypt needs to have an appropriate Block Size for the encryption algorithm.

As far as I remember: a multiple of 16 Bytes for AES.

If the Data you’re going to de/encrypt is not a “full Block size”, the various suppliers again use different approaches to “fill up” or “handle” the last block.

I think to remember… that MBS fills up with Zeros (not quite sure if that’s correct…). While I know that Einhugur just returns the “not filled up block” as-is (unencrypted).

So what happens if you AES-Encrypt Data (LenB: 18) using Einhugur (let’s assume a Key of valid LenB: 16|24|32):
The first 16 Bytes are being AES-Encrypted, then the remaining 2 Bytes are just being appended as-is.
You end up with unencrypted data of an “invalid block size” (Note: I’m not saying Einhugur is wrong - it’s the data being used that’s wrong, so the Plugin has to do “something”).

If you then try to decrypt that using MBS you need to:

  • detect that the encrypted Data is not a multiple of 16: e.g. by (LenB(encData) mod 16)
  • just decrypt the “full blocks” with MBS, then append the remaining Bytes “as-they-are”

Because if you just try to decrypt the “invalid sized encrypted data”, MBS might (I don’t know what MBS is doing in such a situation) fill up with ChrB(0) to the next full block (or just fail, because you’ve supplied invalid data), and decrypt that - which will obviously be different.

Long story short:

  • make sure all Keys/Secrets/… have the correct/expected “(block) length” for the used algorithm
  • don’t forget that the Data to-be-de/encrypted also needs to be of a valid “block size”!
  • if not… then you need to know how the supplier you’ve used has handled the “invalid data”, and using a “different supplier” to decrypt, you have to make the appropriate steps to get it back.

I hope I remember that more or less correct… it’s been a while when I’ve been in a similar situation and had to compare how Einhugur and MBS handle AES encryption with “invalid sized data”.

[quote=470587:@Kevin Gale]Check the length of your password / secret.
If the length does not match what the encryption algorithm requires, the plugin / module will either be truncating it or padding it to the required size. I imagine different suppliers will use a different approach for this.[/quote]
Ah - I remember how MBS and Einhugur handle the AES_ECB and AES_CBC key.
MBS doesn’t allow an invalid length for the key. However, Einhugur does…
But what does Einhugur do if the Key’s LenB is not 16,24,32? That’s the big question in your game.

  • smaller than 16: it uses “0000000000000000” (yes, the Number 0, not ChrB(0)). So any invalid key with LenB<16 will have the very same result, as it’s using the same key effectively for encryption :wink:
  • 16 and < 24: it uses MidB(key, 1, 16)

  • 24 and < 32: it uses MidB(key, 1, 24)

  • 32: it uses MidB(key, 1, 32)

So again… if you “by mistake” have AES-encrypted Data (of valid size) with Einhugur, but used an “invalid length” key of LenB=12 - then you now know how to decrypt that using MBS :wink:

Otherwise… again: make sure of the lengths.

My suspiction is in your example above that the data you’re encrypting as a test has an invalid “block size”.

You probably want something along these lines:

  • don’t encrypt your kInputText!
  • first make sure you’ve got a valid block size for the Data you’re going to encrypt
  • only encrypt valid sized data
Dim sData As String = kInputText 
//Fill up to a valid block size
const AES_BLOCK_SIZE = 16
Dim iRemainingBytes As Integer = (LenB(sData) mod AES_BLOCK_SIZE)
for i As Integer = iRemainingBytes to AES_BLOCK_SIZE - 1 - 1
  sData = sData + ChrB(0)
next
sData = sData + ChrB(iRemainingBytes)

//now encrypt sData - not kInputText

//Note: after Decrypting, look at the last Byte
//it will tell you how many ChrB(0) have been added to fill the required AES Block Size
//cut that away again from the result of the Decryption to get the decrypted original data

Yes. Firstly I tried all your samples including Your AES Example. It ran flawlessly on macOS, but gave 21 compile errors on Windows 10 re “Declares directly into the runtime via Lib “” are no longer allowed” and “Objective–C declares can only be called when building for iOS or OS X”.

When I bracketed these methods with #If TargetiOS or TargetMacOS the example was abe to run, but then it caused a break in CommonCryptoMB.Hash and CommonCryptoMB.Crypt leading to a nil encryption result.

I would love to have it all run in the MBS Encryption Kit.

It‘s not the same…

Again… maybe invalid block size of data-to-be-encrypted?

If you can use symmetric encryption then you could do it in pure Xojo code with RC4: https://forum.xojo.com/conversation/post/110903

cyphertext = RC4(plaintext, key)
and
plaintext = RC4(cyphertext, key)

Here is the final code to encrypt and decrypt across desktop, web, console AND iOS using MBS Encryption plugins and the MBS Encryption Kit.

Sorry I cannot include a full example since it requires a purchase from MBS. I have sent full working examples for desktop, web, console and iOS to Christian in case he wants to include it within his product.

I have only included encryption and decryption here, but I have also used it in conjunction with Christians ZipMBS and iOS Zip routines to allow your text or binary data to be compressed before encryption, if desired. This also works cross desktop, web, console and iOS.

I have used this zipping and compressing to successfully send a componentised SQL command as JSON from iOS to Aloe Express (runs on desktop, web or console) via an HTTP Socket, where it runs the SQL Command (SELECT, INSERT, UPDATE or DELETE), and returns the results (after JSON’ing the RecordSet and error message, zipping and encrypting) then undoing it all again on the iOS device, to return a RecordSet to your iOS app as if you had just retrieved it from a local SQLite database!

[code]Protected Function Encrypt(UnencryptedData As Xojo.Core.MemoryBlock, PasswordKey As Text, IVKey As Text, SaltSize As Integer = 8) as Text
#If TargetiOS Then 'iOS and macOS only
// create the salt and generate a 64 character CheckHash
Dim Salt As Xojo.Core.MemoryBlock = CommonCryptoMB.generateBytes1(SaltSize)
Dim CheckHash As Xojo.Core.MemoryBlock = CommonCryptoMB.Hash(CommonCryptoMB.Hashes.SHA512, UnencryptedData)

// get hash of key
Dim KeyHash As Xojo.Core.MemoryBlock = HashKey(PasswordKey)

// set to encrypt
Dim Operation As CommonCryptoMB.CryptoOperation = CommonCryptoMB.CryptoOperation.Encrypt

// AES
Dim Algorithm As CommonCryptoMB.CryptoAlgorithm = CommonCryptoMB.CryptoAlgorithm.AES

// CBC mode and padding
Dim Options As Integer = BitWiseOR(CommonCryptoMB.kCCOptionPKCS7Padding, CommonCryptoMB.kCCOptionCBCMode)

// IV Key
Dim IV As Xojo.Core.MemoryBlock
If IVKey <> "" Then 'leave as nil?
  IV = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(IVKey)
End If

// get data to encrypt as memoryblock
Dim EncryptedData As Xojo.Core.MemoryBlock = CommonCryptoMB.Crypt(Operation, Algorithm, Options, KeyHash, UnencryptedData, IV)

// append salt and check Hash
Dim tempMutableMemoryBlock As New Xojo.Core.MutableMemoryBlock(Salt)
tempMutableMemoryBlock.Append(EncryptedData)
tempMutableMemoryBlock.Append(CheckHash)

Return CommonCryptoMB.EncodeBase64Memory(New Xojo.Core.MemoryBlock(tempMutableMemoryBlock))

#Else 'macOS, Windows, Linux, desktop, web, console only, but not iOS
// via MBS Encryption Plugin for Mac/Windows/Linux
Dim Salt As String = RandomBytesStringMBS(SaltSize)
Var tempMemoryBlock As MemoryBlock = UnencryptedData.Data 'convert from Xojo.Core.MemoryBlock to MemoryBlock to String
Dim InputData As String = tempMemoryBlock.StringValue(0, UnencryptedData.Size) 'cannot use TextEncoding.UTF8 as it might be binary data
tempMemoryBlock = nil 'clear the RAM
Dim CheckHash As String = SHA512MBS.Hash(InputData)

// get key
Dim KeyHash As String = SHA256MBS.Hash(PasswordKey)

// encrypt
Dim ecipher As CipherMBS = CipherMBS.aes_256_cbc

// IV Key
Dim IV As MemoryBlock
If IVKey <> "" Then 'leave as nil?
  IV = IVKey
End If

If ecipher.EncryptInit(KeyHash, IV) Then 'change Nil to IV later!
  ecipher.Padding = True
  Dim EncryptedData As String = ecipher.ProcessString(InputData) + ecipher.FinalizeAsString
  
  Return EncodeBase64MBS(Salt + EncryptedData + CheckHash, 0, "").ToText
  
Else
  Return ""
End If

#EndIf

End Function[/code]

[code]Protected Function Decrypt(EncryptedBase64 As Text, PasswordKey As Text, IVKey As Text, SaltSize As Integer = 8) as Text
#If TargetiOS Then 'iOS and macOS only
// don’t need to create Salt

// get hash of key
Dim KeyHash As Xojo.Core.MemoryBlock = HashKey(PasswordKey)

// set to decrypt
Dim Operation As CommonCryptoMB.CryptoOperation = CommonCryptoMB.CryptoOperation.Decrypt

// AES
Dim Algorithm As CommonCryptoMB.CryptoAlgorithm = CommonCryptoMB.CryptoAlgorithm.AES

// CBC mode and padding
Dim Options As Integer = BitWiseOR(CommonCryptoMB.kCCOptionPKCS7Padding, CommonCryptoMB.kCCOptionCBCMode)

// IV Key
Dim IV As Xojo.Core.MemoryBlock
If IVKey <> "" Then 'leave as nil?
  IV = Xojo.Core.TextEncoding.UTF8.ConvertTextToData(IVKey)
End If

// get data to decrypt as memoryblock
Dim EncryptedData As Xojo.Core.MemoryBlock = CommonCryptoMB.DecodeBase64Memory(EncryptedBase64)

// remove salt and check Hash
Dim CheckHash As Xojo.Core.MemoryBlock = EncryptedData.Right(64)
EncryptedData = EncryptedData.Mid(SaltSize, EncryptedData.Size - 64 - SaltSize) 'remove Salt and CheckHash, zero-based so start from character #8

Dim DecryptedData As Xojo.Core.MemoryBlock = CommonCryptoMB.Crypt(Operation, Algorithm, Options, KeyHash, EncryptedData, IV)

If CheckHash <> CommonCryptoMB.Hash(CommonCryptoMB.Hashes.SHA512, DecryptedData) Then 'check that the Hash is still correct ie the text has not been tampered with
  Return ""
End If

'Return DecryptedData
Return CommonCryptoMB.EncodeBase64Memory(DecryptedData)

#Else 'macOS, Windows, Linux, desktop, web, console only, but not iOS
// don’t need to create Salt

// get hash of key
Dim KeyHash As String = SHA256MBS.Hash(PasswordKey)

// get data to decrypt as String
Dim EncryptedData As String = DecodeBase64MBS(EncryptedBase64)

// remove salt and check Hash
Dim CheckHash As String = EncryptedData.RightB(64)
EncryptedData = EncryptedData.MiddleBytes(SaltSize, EncryptedData.LenB - 64 - SaltSize) 'remove Salt and CheckHash, zero-based so start from character #8

// IV Key
Dim IV As MemoryBlock
If IVKey <> "" Then 'leave as nil?
  IV = IVKey
End If

// now decrypt
Dim dcipher As CipherMBS = CipherMBS.aes_256_CBC
If dcipher.DecryptInit(KeyHash, IV) Then 'change Nil to IV later!
  dcipher.Padding = True
  
  Dim DecryptedData As String = dcipher.ProcessString(EncryptedData) + dcipher.FinalizeAsString
  
  If CheckHash <> SHA512MBS.Hash(DecryptedData) Then 'check that the Hash is still correct ie the text has not been tampered with
    Return ""
  End If
  
  Return EncodeBase64MBS(DecryptedData, 0, "").ToText 'use Base64 since Xojo.Core.MemoryBlock sometimes gets corrupted
  
End If

#EndIf

End Function
[/code]

On a side note, once iOS has api2, I’ll make sure M_Crypto works there too.