Attribute VB_Name = "basRadix64"
Option Explicit
Option Base 0
' basRadix64: Radix 64 en/decoding functions
' Version 6. 20 November 2003. Added error handling for empty arrays.
' Version 5. 10 August 2003. Added EncodeBytes64() and DecodeBytes64()
' functions that "do it properly" using a Byte array for
' binary data and a String for textual data.
' Version 4. 17 August 2002 re-write even faster using Byte arrays
' and StrConv function. Thanks to Chris Thompson for this
' and for other much appreciated advice incorporated here.
' Version 3.1: 13 August 2002 mod to DecodeStr64 function
' to cope with invalid characters.
' Thanks to Seth Perelman for this.
' Version 3. Published January 2002 with even faster SHR/SHL functions
' and using Mid$ function instead of appending to strings.
' Version 2. Published 12 May 2001
' Version 1. Published 28 December 2000
'************************* COPYRIGHT NOTICE*************************
' This code was originally written in Visual Basic by David Ireland
' and is copyright (c) 2000-3 D.I. Management Services Pty Limited,
' all rights reserved.
' You are free to use this code as part of your own applications
' provided you keep this copyright notice intact and acknowledge
' its authorship with the words:
' "Contains cryptography software by David Ireland of
' DI Management Services Pty Ltd <www.di-mgt.com.au>."
' This code may only be used as part of an application. It may
' not be reproduced or distributed separately by any means without
' the express written permission of the author.
' David Ireland and DI Management Services Pty Limited make no
' representations concerning either the merchantability of this
' software or the suitability of this software for any particular
' purpose. It is provided "as is" without express or implied
' warranty of any kind.
' Please forward comments or bug reports to <code@di-mgt.com.au>.
' The latest version of this source code can be downloaded from
' www.di-mgt.com.au/crypto.html.
' Credit where credit is due:
' Some parts of this VB code are based on original C code
' by Carl M. Ellison. See "cod64.c" published 1995.
'****************** END OF COPYRIGHT NOTICE*************************
Private aDecTab(255) As Integer
Private aEncTab(63) As Byte
Public Function EncodeBytes64(abBytes() As Byte) As String
' Return base64 encoding of Byte array.
' Does not insert CRLFs. Just returns one long string,
' so it's up to the user to add line breaks or other formatting.
Dim sOutput As String
Dim abOutput() As Byte
Dim sLast As String
Dim b(3) As Byte
Dim j As Integer
Dim i As Long, nLen As Long, nQuants As Long
Dim iIndex As Long
'Set up error handler to catch empty array
On Error GoTo ArrayIsEmpty
nLen = UBound(abBytes) - LBound(abBytes) + 1
nQuants = nLen \ 3
iIndex = 0
Call MakeEncTab
If (nQuants > 0) Then
ReDim abOutput(nQuants * 4 - 1)
' Now start reading in 3 bytes at a time
For i = 0 To nQuants - 1
For j = 0 To 2
b(j) = abBytes((i * 3) + j)
Next
Call EncodeQuantumB(b)
abOutput(iIndex) = b(0)
abOutput(iIndex + 1) = b(1)
abOutput(iIndex + 2) = b(2)
abOutput(iIndex + 3) = b(3)
iIndex = iIndex + 4
Next
sOutput = StrConv(abOutput, vbUnicode)
End If
' Cope with odd bytes
' (no real performance hit by using strings here)
Select Case nLen Mod 3
Case 0
sLast = ""
Case 1
b(0) = abBytes(nLen - 1)
b(1) = 0
b(2) = 0
Call EncodeQuantumB(b)
sLast = StrConv(b(), vbUnicode)
' Replace last 2 with =
sLast = Left(sLast, 2) & "=="
Case 2
b(0) = abBytes(nLen - 2)
b(1) = abBytes(nLen - 1)
b(2) = 0
Call EncodeQuantumB(b)
sLast = StrConv(b(), vbUnicode)
' Replace last with =
sLast = Left(sLast, 3) & "="
End Select
EncodeBytes64 = sOutput & sLast
ArrayIsEmpty:
End Function
Public Function DecodeBytes64(sEncoded As String) As Variant
' Return Byte array of decoded binary values given base64 string
' Ignores any chars not in the 64-char subset
Dim abDecoded() As Byte
Dim d(3) As Byte
Dim C As Integer ' NB Integer to catch -1 value
Dim di As Integer
Dim i As Long
Dim nLen As Long
Dim iIndex As Long
nLen = Len(sEncoded)
If nLen < 4 Then
' Return an empty array
DecodeBytes64 = abDecoded
Exit Function
End If
ReDim abDecoded(((nLen \ 4) * 3) - 1)
iIndex = 0
di = 0
Call MakeDecTab
' Read in each char in turn
For i = 1 To Len(sEncoded)
C = CByte(Asc(Mid(sEncoded, i, 1)))
C = aDecTab(C)
If C >= 0 Then
d(di) = CByte(C)
di = di + 1
If di = 4 Then
abDecoded(iIndex) = SHL2(d(0)) Or (SHR4(d(1)) And &H3)
iIndex = iIndex + 1
abDecoded(iIndex) = SHL4(d(1) And &HF) Or (SHR2(d(2)) And &HF)
iIndex = iIndex + 1
abDecoded(iIndex) = SHL6(d(2) And &H3) Or d(3)
iIndex = iIndex + 1
If d(3) = 64 Then
iIndex = iIndex - 1
abDecoded(iIndex) = 0
End If
If d(2) = 64 Then
iIndex = iIndex - 1
abDecoded(iIndex) = 0
End If
di = 0
End If
End If
Next i
' Trim to correct length
ReDim Preserve abDecoded(iIndex - 1)
DecodeBytes64 = abDecoded
End Function
Public Function EncodeStr64(sInput As String) As String
' Return radix64 encoding of string of binary values
' Does not insert CRLFs. Just returns one long string,
' so it's up to the user to add line breaks or other formatting.
' Version 4: Use Byte array and StrConv - much faster
Dim abOutput() As Byte ' Version 4: now a Byte array
Dim sLast As String
Dim b(3) As Byte ' Version 4: Now 3 not 2
Dim j As Integer
Dim i As Long, nLen As Long, nQuants As Long
Dim iIndex As Long
EncodeStr64 = ""
nLen = Len(sInput)
nQuants = nLen \ 3
iIndex = 0
Call MakeEncTab
If (nQuants > 0) Then
ReDim abOutput(nQuants * 4 - 1)
' Now start reading in 3 bytes at a time
For i = 0 To nQuants - 1
For j = 0 To 2
b(j) = Asc(Mid(sInput, (i * 3) + j + 1, 1))
Next
Call EncodeQuantumB(b)
abOutput(iIndex) = b(0)
abOutput(iIndex + 1) = b(1)
abOutput(iIndex + 2) = b(2)
abOutput(iIndex + 3) = b(3)
iIndex = iIndex + 4
Next
EncodeStr64 = StrConv(abOutput, vbUnicode)
End If
' Cope with odd bytes
' (no real performance hit by using strings here)
Select Case nLen Mod 3
Case 0
sLast = ""
Case 1
b(0) = Asc(Mid(sInput, nLen, 1))
b(1) = 0
b(2) = 0
Call EncodeQuantumB(b)
sLast = StrConv(b(), vbUnicode)
' Replace last 2 with =
sLast = Left(sLast, 2) & "=="
Case 2
b(0) = Asc(Mid(sInput, nLen - 1, 1))
b(1) = Asc(Mid(sInput, nLen, 1))
b(2) = 0
Call EncodeQuantumB(b)
sLast = StrConv(b(), vbUnicode)
' Replace last with =
sLast = Left(sLast, 3) & "="
End Select
EncodeStr64 = EncodeStr64 & sLast
End Function
Public Function DecodeStr64(sEncoded As String) As String
' Return string of decoded binary values given radix64 string
' Ignores any chars not in the 64-char subset
' Version 4: Use Byte array and StrConv - much faster
Dim abDecoded() As Byte 'Version 4: Now a Byte array
Dim d(3) As Byte
Dim C As Integer ' NB Integer to catch -1 value
Dim di As Integer
Dim i As Long
Dim nLen As Long
Dim iIndex As Long
nLen = Len(sEncoded)
If nLen < 4 Then
Exit Function
End If
ReDim abDecoded(((nLen \ 4) * 3) - 1) 'Version 4: Now base zero
iIndex = 0 ' Version 4: Changed to base 0
di = 0
Call MakeDecTab
' Read in each char in turn
For i = 1 To Len(sEncoded)
C = CByte(Asc(Mid(sEncoded, i, 1)))
C = aDecTab(C)
If C >= 0 Then
d(di) = CByte(C) ' Version 3.1: add CByte()
di = di + 1
If di = 4 Then
abDecoded(iIndex) = SHL2(d(0)) Or (SHR4(d(1)) And &H3)
iIndex = iIndex + 1
abDecoded(iIndex) = SHL4(d(1) And &HF) Or (SHR2(d(2)) And &HF)
iIndex = iIndex + 1
abDecoded(iIndex) = SHL6(d(2) And &H3) Or d(3)
iIndex = iIndex + 1
If d(3) = 64 Then
iIndex = iIndex - 1
abDecoded(iIndex) = 0
End If
If d(2) = 64 Then
iIndex = iIndex - 1
abDecoded(iIndex) = 0
End If
di = 0
End If
End If
Next i
' Convert to a string
DecodeStr64 = StrConv(abDecoded(), vbUnicode)
' Remove any unwanted trailing chars
DecodeStr64 = Left(DecodeStr64, iIndex)
End Function
Private Sub EncodeQuantumB(b() As Byte)
' Expects at least 4 bytes in b, i.e. Dim b(3) As Byte
Dim b0 As Byte, b1 As Byte, b2 As Byte, b3 As Byte
b0 = SHR2(b(0)) And &H3F
b1 = SHL4(b(0) And &H3) Or (SHR4(b(1)) And &HF)
b2 = SHL2(b(1) And &HF) Or (SHR6(b(2)) And &H3)
b3 = b(2) And &H3F
b(0) = aEncTab(b0)
b(1) = aEncTab(b1)
b(2) = aEncTab(b2)
b(3) = aEncTab(b3)
End Sub
Private Function MakeDecTab()
' Set up Radix 64 decoding table
Dim t As Integer
Dim C As Integer
For C = 0 To 255
aDecTab(C) = -1
Next
t = 0
For C = Asc("A") To Asc("Z")
aDecTab(C) = t
t = t + 1
Next
For C = Asc("a") To Asc("z")
aDecTab(C) = t
t = t + 1
Next
For C = Asc("0") To Asc("9")
aDecTab(C) = t
t = t + 1
Next
C = Asc("+")
aDecTab(C) = t
t = t + 1
C = Asc("/")
aDecTab(C) = t
t = t + 1
C = Asc("=") ' flag for the byte-deleting char
aDecTab(C) = t ' should be 64
End Function
Private Function MakeEncTab()
' Set up Radix 64 encoding table in bytes
Dim i As Integer
Dim C As Integer
i = 0
For C = Asc("A") To Asc("Z")
aEncTab(i) = C
i = i + 1
Next
For C = Asc("a") To Asc("z")
aEncTab(i) = C
i = i + 1
Next
For C = Asc("0") To Asc("9")
aEncTab(i) = C
i = i + 1
Next
C = Asc("+")
aEncTab(i) = C
i = i + 1
C = Asc("/")
aEncTab(i) = C
i = i + 1
End Function
' Version 3: ShiftLeft and ShiftRight functions improved.
Private Function SHL2(ByVal bytValue As Byte) As Byte
' Shift 8-bit value to left by 2 bits
' i.e. VB equivalent of "bytValue << 2" in C
SHL2 = (bytValue * &H4) And &HFF
End Function
Private Function SHL4(ByVal bytValue As Byte) As Byte
' Shift 8-bit value to left by 4 bits
' i.e. VB equivalent of "bytValue << 4" in C
SHL4 = (bytValue * &H10) And &HFF
End Function
Private Function SHL6(ByVal bytValue As Byte) As Byte
' Shift 8-bit value to left by 6 bits
' i.e. VB equivalent of "bytValue << 6" in C
SHL6 = (bytValue * &H40) And &HFF
End Function
Private Function SHR2(ByVal bytValue As Byte) As Byte
' Shift 8-bit value to right by 2 bits
' i.e. VB equivalent of "bytValue >> 2" in C
SHR2 = bytValue \ &H4
End Function
Private Function SHR4(ByVal bytValue As Byte) As Byte
' Shift 8-bit value to right by 4 bits
' i.e. VB equivalent of "bytValue >> 4" in C
SHR4 = bytValue \ &H10
End Function
Private Function SHR6(ByVal bytValue As Byte) As Byte
' Shift 8-bit value to right by 6 bits
' i.e. VB equivalent of "bytValue >> 6" in C
SHR6 = bytValue \ &H40
End Function