After reading up on encryption algorithms and getting some really good advice from Maarten, I see that the ChaCha20 encryption algorithm has the potential to provide strong symmetric encryption AND good performance on older computers that lack specialized AES encryption/decryption instructions.
Is there a good example of VB.NET code that implements the ChaCha20 encryption algorithm in VB.NET?
VB.NET may not be ideal because it lacks a lot of specific instructions, for instance for circular bit rotation on unsigned integers (which are a key part of ChaCha20), so the question is can this algorithm still be implemented well in VB.NET?
After trying to write the ChaCha20 encryption algorithm in VB.NET, it seems that it will work quite well. Please see the code below.
There is no circular bit rotation for integers in VB.NET, but in the code you will find a solid workaround for achieving circular bit rotation.
I have added a test subroutine that tests the code against some official ChaCha20 test vectors you can find in the final spec here.
PLEASE TAKE IN TO ACCOUNT THESE GUIDELINES BEFORE USING THIS CODE:
It has not been assessed by encryption experts and may contain security issues, especially on older CPUs that are chock-full of Meltdown and Spectre-type security flaws as well as a whole bunch more security flaws.
Feedback to improve the code and performance is more than welcome, you can use this code freely and unrestricted.
Until this code is assessed by experience encryption algorithm developers, use it where you are looking for obfuscation rather than strong encryption. (it is likely to offer this already, but zero guarantees)
Make sure you protect the key you use, never store it unencrypted.
Never ever (ever ever ever) use the same Nonce + Counter combination twice. You need these to decrypt the information, no need to protect these from an adversary put all your efforts in to making sure your key is safe.
The subs for Encrypting and Decrypting are exactly the same, as this is symmetric encryption (that is why the sub names contain EncDec in the name).
Don't forget to check the Remove integer overflow checks-checkbox, see here how to do so.
Enjoy and let me know what needs to be improved!
Sub ValidateChaCha20AlgorithmAgainstStandard()
'See final spec for ChaCha20 with test vectors here: https://datatracker.ietf.org/doc/rfc7539/
Dim Key As String
Dim Nonce As String
Dim Counter As UInt32
Dim HowMany As UInt32
'Final spec for two blocks!
Key = "03020100-07060504-0b0a0908-0f0e0d0c-13121110-17161514-1b1a1918-1f1e1d1c"
Nonce = "00000000-4a000000-00000000"
Counter = 1
HowMany = 2
Dim KeyStream() As Byte
KeyStream = ChaCha20_GenerateEncDecByteStream(Key, Nonce, Counter, HowMany)
Dim KeyStreamOutputHexString As String
KeyStreamOutputHexString = ByteArrayToString(KeyStream)
Dim ValidatedKeyStreamOutput As String
ValidatedKeyStreamOutput = "224f51f3401bd9e12fde276fb8631ded8c131f823d2c06e27e4fcaec9ef3cf788a3b0aa372600a92b57974cded2b9334794cba40c63e34cdea212c4cf07d41b769a6749f3f630f4122cafe28ec4dc47e26d4346d70b98c73f3e9c53ac40c5945398b6eda1a832c89c167eacd901d7e2bf363740373201aa188fbbce83991c4ed"
Dim StringCompareThatMustFail As String
StringCompareThatMustFail = "I will fail for sure!"
If KeyStreamOutputHexString.Equals(StringCompareThatMustFail) = False Then Debug.Print("String compare works properly")
If KeyStreamOutputHexString.Equals(StringCompareThatMustFail) = True Then Debug.Print("String compare has FAILED DO NOT TRUST this verification!!!")
If KeyStreamOutputHexString.Equals(ValidatedKeyStreamOutput) = True Then Debug.Print("ChaCha20 algorithm has passed the validation check, output is 100% correct")
If KeyStreamOutputHexString.Equals(ValidatedKeyStreamOutput) = False Then Debug.Print("ChaCha20 algorithm has FAILED the validation check do NOT USE the algorithm!!!")
'Now we test the bytestream encoding!
Dim PlainText As String
Dim PlainByteStream As Byte()
PlainText = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, suncreen would be it."
PlainByteStream = UTF8StringToBytes(PlainText)
Dim EncryptedStream As Byte()
Dim DecryptedStream As Byte()
Dim DecryptedString As String
EncryptedStream = ChaCha20_EncDecByteStream(PlainByteStream, KeyStream)
DecryptedStream = ChaCha20_EncDecByteStream(EncryptedStream, KeyStream)
DecryptedString = UTF8BytesToString(DecryptedStream)
If DecryptedString.Equals(PlainText) = True Then Debug.Print("2dn ChaCha20 validation check passed, algorithm has SUCCESFULLY performed a full encryption/decryption round trip yielding exactly the source data!")
End Sub
Function ChaCha20_EncDecByteStream(StreamToEncDec() As Byte, KeyStream() As Byte) As Byte()
Dim b As Byte
Dim ProcessedStream As Byte()
Dim LoopCounter As Integer
ReDim ProcessedStream(StreamToEncDec.Length - 1)
For LoopCounter = 0 To (StreamToEncDec.Length - 1)
ProcessedStream(LoopCounter) = StreamToEncDec(LoopCounter) Xor KeyStream(LoopCounter)
Next
Return ProcessedStream
End Function
Function ChaCha20_GenerateEncDecByteStream(Key As String, Nonce As String, CounterStartPosition As UInt32, HowManyBlocks As UInt32) As Byte()
'This function creates a bytestream with the ChaCha20 encryption algorithm that can be used for further processing
'The stream is always in units of 64 bytes (512 bits).
'The key and the nonce are Hexadecimal strings with every 8 Hex characters (32 bits) separated by a - for readability 'coz that is just very convenient to use :)
Dim KeyStringArray() As String
Dim NonceStringArray() As String
KeyStringArray = Split(Key, "-")
NonceStringArray = Split(Nonce, "-")
Dim ChaChaStartBlock(15) As UInt32
'See specified under https://datatracker.ietf.org/doc/html/rfc7539
' The first four words (0-3) are constants: 0x61707865, 0x3320646e, 0x79622d32, 0x6b206574.
'Here we set the constants
ChaChaStartBlock(0) = &H61707865
ChaChaStartBlock(1) = &H3320646E
ChaChaStartBlock(2) = &H79622D32
ChaChaStartBlock(3) = &H6B206574
'Here we set the key
ChaChaStartBlock(4) = CUInt("&H" & KeyStringArray(0))
ChaChaStartBlock(5) = CUInt("&H" & KeyStringArray(1))
ChaChaStartBlock(6) = CUInt("&H" & KeyStringArray(2))
ChaChaStartBlock(7) = CUInt("&H" & KeyStringArray(3))
ChaChaStartBlock(8) = CUInt("&H" & KeyStringArray(4))
ChaChaStartBlock(9) = CUInt("&H" & KeyStringArray(5))
ChaChaStartBlock(10) = CUInt("&H" & KeyStringArray(6))
ChaChaStartBlock(11) = CUInt("&H" & KeyStringArray(7))
'Here we set the initial counter position
ChaChaStartBlock(12) = CounterStartPosition
'Here we set the Nonce
ChaChaStartBlock(13) = CUInt("&H" & NonceStringArray(0))
ChaChaStartBlock(14) = CUInt("&H" & NonceStringArray(1))
ChaChaStartBlock(15) = CUInt("&H" & NonceStringArray(2))
Debug.Print("Start block:")
Debug.Print(Hex(ChaChaStartBlock(0)) & " " & Hex(ChaChaStartBlock(1)) & " " & Hex(ChaChaStartBlock(2)) & " " & Hex(ChaChaStartBlock(3)))
Debug.Print(Hex(ChaChaStartBlock(4)) & " " & Hex(ChaChaStartBlock(5)) & " " & Hex(ChaChaStartBlock(6)) & " " & Hex(ChaChaStartBlock(7)))
Debug.Print(Hex(ChaChaStartBlock(8)) & " " & Hex(ChaChaStartBlock(9)) & " " & Hex(ChaChaStartBlock(10)) & " " & Hex(ChaChaStartBlock(11)))
Debug.Print(Hex(ChaChaStartBlock(12)) & " " & Hex(ChaChaStartBlock(13)) & " " & Hex(ChaChaStartBlock(14)) & " " & Hex(ChaChaStartBlock(15)))
'Now we create a byte array of the size we will need
Dim ChaChaByteArray() As Byte
ReDim ChaChaByteArray(HowManyBlocks * 64 - 1)
Dim ByteArrayFillLoop As Integer
Dim ChaChaResultBlock(15) As UInt32
Dim InnerLoop As Integer
Dim FourByteArray(3) As Byte
Dim FourByteArrayReverseOrder(3) As Byte
For ByteArrayFillLoop = 0 To (HowManyBlocks - 1)
ChaChaStartBlock(12) = CounterStartPosition + ByteArrayFillLoop
ChaChaResultBlock = ChaCha20_GenerateEncDecBlock(ChaChaStartBlock)
Debug.Print("Result block:")
Debug.Print(Hex(ChaChaResultBlock(0)) & " " & Hex(ChaChaResultBlock(1)) & " " & Hex(ChaChaResultBlock(2)) & " " & Hex(ChaChaResultBlock(3)))
Debug.Print(Hex(ChaChaResultBlock(4)) & " " & Hex(ChaChaResultBlock(5)) & " " & Hex(ChaChaResultBlock(6)) & " " & Hex(ChaChaResultBlock(7)))
Debug.Print(Hex(ChaChaResultBlock(8)) & " " & Hex(ChaChaResultBlock(9)) & " " & Hex(ChaChaResultBlock(10)) & " " & Hex(ChaChaResultBlock(11)))
Debug.Print(Hex(ChaChaResultBlock(12)) & " " & Hex(ChaChaResultBlock(13)) & " " & Hex(ChaChaResultBlock(14)) & " " & Hex(ChaChaResultBlock(15)))
'Here we copy the block into the byte array
For InnerLoop = 0 To 15
FourByteArray = BitConverter.GetBytes(ChaChaResultBlock(InnerLoop))
If BitConverter.IsLittleEndian = False Then
FourByteArrayReverseOrder(0) = FourByteArray(3)
FourByteArrayReverseOrder(1) = FourByteArray(2)
FourByteArrayReverseOrder(2) = FourByteArray(1)
FourByteArrayReverseOrder(3) = FourByteArray(0)
FourByteArray = FourByteArrayReverseOrder
End If
Buffer.BlockCopy(FourByteArray, 0, ChaChaByteArray, ByteArrayFillLoop * 64 + InnerLoop * 4, 4)
Next
Next
Return ChaChaByteArray
End Function
Function ChaCha20_GenerateEncDecBlock(CC20M As UInt32()) As UInt32()
Dim M(15) As UInt32
Array.Copy(CC20M, M, M.Length)
Dim R10CTR As Integer
For R10CTR = 1 To 10
Call ChaCha20_QR(M(0), M(4), M(8), M(12))
Call ChaCha20_QR(M(1), M(5), M(9), M(13))
Call ChaCha20_QR(M(2), M(6), M(10), M(14))
Call ChaCha20_QR(M(3), M(7), M(11), M(15))
Call ChaCha20_QR(M(0), M(5), M(10), M(15))
Call ChaCha20_QR(M(1), M(6), M(11), M(12))
Call ChaCha20_QR(M(2), M(7), M(8), M(13))
Call ChaCha20_QR(M(3), M(4), M(9), M(14))
Next
Dim CTR As Integer
For CTR = 0 To 15
M(CTR) = M(CTR) + CC20M(CTR)
Next
Return M
End Function
Sub ChaCha20_QR(ByRef a As UInt32, ByRef b As UInt32, ByRef c As UInt32, ByRef d As UInt32)
a = a + b
d = d Xor a
d = d << 16 Or d >> 16
c = c + d
b = b Xor c
b = b << 12 Or b >> 20
a = a + b
d = d Xor a
d = d << 8 Or d >> 24
c = c + d
b = b Xor c
b = b << 7 Or b >> 25
End Sub
Function ByteArrayToString(ba() As Byte) As String
Dim hex As New StringBuilder(ba.Length * 2)
Dim b As Byte
For Each b In ba
hex.AppendFormat("{0:x2}", b)
Next
Return hex.ToString()
End Function
Private Function UTF8StringToBytes(
ByVal str As String) As Byte()
Return System.Text.Encoding.UTF8.GetBytes(str)
End Function
Private Function UTF8BytesToString(
ByVal bytes() As Byte) As String
Return System.Text.Encoding.UTF8.GetString(bytes)
End Function