Base64: Difference between revisions
(Base 64 Encoder Function written in Native NSIS) |
m (comment fail.. two > four) |
||
(2 intermediate revisions by 2 users not shown) | |||
Line 1: | Line 1: | ||
== BASE64 Encoding/Decoding Functions == | |||
As a result of needing to send data in a URL from my NSIS Installer, and because I didn't want to include another dependency, 99999999 wrote the below Base64 Encoder. | |||
'''Please note:''' this header depends on the [[CharToASCII]] header. | |||
Forum thread: http://forums.winamp.com/showthread.php?t=297879 "Base64 Encoder in NSIS" | |||
${Base64_Encode} "THIS WILL BE ENCODED" | Usage example as follows: | ||
Pop $0 | <highlight-nsis> | ||
; $0 now equals VEhJUyBXSUxMIEJFIEVOQ09ERUQ= | !include "CharToASCII.nsh" | ||
!include "Base64.nsh" | |||
OutFile "$%temp%\temp.exe" | |||
Section | |||
${Base64_Encode} "THIS WILL BE ENCODED" | |||
Pop $0 | |||
; $0 now equals VEhJUyBXSUxMIEJFIEVOQ09ERUQ= | |||
MessageBox MB_OK "$0" | |||
SectionEnd | |||
</highlight-nsis> | |||
Also there is a variant for encoding in a URL safe form, ${Base64_URLEncode}, which just uses a different encoding table, (but a standard one,) from the original version. I don't know how this works with Binary Data, but it works great with string data, which you need to put into a consistent form. | |||
Animaether added decoding routines. | |||
Forum thread: http://forums.winamp.com/showthread.php?t=322673 "Base64 decoder" | |||
<highlight-nsis> | |||
!include "CharToASCII.nsh" | |||
!include "Base64.nsh" | |||
OutFile "$%temp%\temp.exe" | |||
Section | |||
${Base64_Decode} "VEhJUyBXSUxMIEJFIERFQ09ERUQ=" | |||
Pop $0 | |||
; $0 now equals "THIS WILL BE DECODED" | |||
MessageBox MB_OK "$0" | |||
SectionEnd | |||
</highlight-nsis> | |||
As with the encoding functions, a URL safe form exists in the form of ${Base64_URLDecode}. | |||
== The header code == | |||
<highlight-nsis> | <highlight-nsis> | ||
!ifndef BASE64_NSH | !ifndef BASE64_NSH | ||
!define BASE64_NSH | !define BASE64_NSH | ||
!define BASE64_ENCODINGTABLE "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | !define BASE64_ENCODINGTABLE "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" | ||
Line 23: | Line 49: | ||
!define BASE64_PADDING "=" | !define BASE64_PADDING "=" | ||
VAR | VAR OCTETVALUE | ||
VAR BASE64TEMP | VAR BASE64TEMP | ||
Line 30: | Line 56: | ||
!macro Base64_Encode _cleartext | !macro Base64_Encode _cleartext | ||
push $R0 | |||
push $R1 | |||
push $R2 | |||
push $0 | |||
push $1 | |||
push $2 | |||
push $3 | |||
push $4 | |||
push $5 | |||
push $6 | |||
push $7 | |||
push `${_cleartext}` | |||
push `${BASE64_ENCODINGTABLE}` | |||
Call Base64_Encode | |||
Pop $BASE64TEMP | |||
Pop $7 | |||
Pop $6 | |||
Pop $5 | |||
Pop $4 | |||
Pop $3 | |||
Pop $2 | |||
Pop $1 | |||
Pop $0 | |||
pop $R2 | |||
pop $R1 | |||
pop $R0 | |||
Push $BASE64TEMP | |||
!macroend | !macroend | ||
!macro Base64_URLEncode _cleartext | !macro Base64_URLEncode _cleartext | ||
push $R0 | |||
push $R1 | |||
push $R2 | |||
push $0 | |||
push $1 | |||
push $2 | |||
push $3 | |||
push $4 | |||
push $5 | |||
push $6 | |||
push $7 | |||
push `${_cleartext}` | |||
push `${BASE64_ENCODINGTABLEURL}` | |||
Call Base64_Encode | |||
Pop $BASE64TEMP | |||
Pop $7 | |||
Pop $6 | |||
Pop $5 | |||
Pop $4 | |||
Pop $3 | |||
Pop $2 | |||
Pop $1 | |||
Pop $0 | |||
pop $R2 | |||
pop $R1 | |||
pop $R0 | |||
Push $BASE64TEMP | |||
!macroend | !macroend | ||
Function Base64_Encode | |||
pop $R2 ; Encoding table | |||
pop $R0 ; Clear Text | |||
StrCpy "$R1" "" # The result | |||
StrLen $1 "$R0" | |||
StrCpy $0 0 | |||
${WHILE} $0 < $1 | |||
# Copy 3 characters, and for each character push their value. | |||
StrCpy $OCTETVALUE 0 | |||
StrCpy $5 $0 | |||
StrCpy $4 "$R0" 1 $5 | |||
${CharToASCII} $4 "$4" | |||
IntOp $OCTETVALUE $4 << 16 | |||
IntOp $5 $5 + 1 | |||
${IF} $5 < $1 | |||
StrCpy $4 "$R0" 1 $5 | |||
${CharToASCII} $4 "$4" | |||
IntOp $4 $4 << 8 | |||
IntOp $OCTETVALUE $OCTETVALUE + $4 | |||
IntOp $5 $5 + 1 | |||
${IF} $5 < $1 | |||
StrCpy $4 "$R0" 1 $5 | |||
${CharToASCII} $4 "$4" | |||
IntOp $OCTETVALUE $OCTETVALUE + $4 | |||
${ENDIF} | |||
${ENDIF} | |||
# Now take the 4 indexes from the encoding table, based on 6bits each of the octet's value. | |||
IntOp $4 $OCTETVALUE >> 18 | |||
IntOp $4 $4 & 63 | |||
StrCpy $5 "$R2" 1 $4 | |||
StrCpy $R1 "$R1$5" | |||
IntOp $4 $OCTETVALUE >> 12 | |||
IntOp $4 $4 & 63 | |||
StrCpy $5 "$R2" 1 $4 | |||
StrCpy $R1 "$R1$5" | |||
StrCpy $6 $0 | |||
StrCpy $7 2 | |||
IntOp $6 $6 + 1 | |||
${IF} $6 < $1 | |||
IntOp $4 $OCTETVALUE >> 6 | |||
IntOp $4 $4 & 63 | |||
StrCpy $5 "$R2" 1 $4 | |||
StrCpy $R1 "$R1$5" | |||
IntOp $7 $7 - 1 | |||
${ENDIF} | |||
IntOp $6 $6 + 1 | |||
${IF} $6 < $1 | |||
IntOp $4 $OCTETVALUE & 63 | |||
StrCpy $5 "$R2" 1 $4 | |||
StrCpy $R1 "$R1$5" | |||
IntOp $7 $7 - 1 | |||
${ENDIF} | |||
# If there is any padding required, we now write that here. | |||
${IF} $7 > 0 | |||
${WHILE} $7 > 0 | |||
StrCpy $R1 "$R1${BASE64_PADDING}" | |||
IntOp $7 $7 - 1 | |||
${ENDWHILE} | |||
${ENDIF} | |||
IntOp $0 $0 + 3 | |||
${ENDWHILE} | |||
Push "$R1" | |||
FunctionEnd | |||
!define Base64_Decode "!insertmacro Base64_Decode" | |||
!define Base64_URLDecode "!insertmacro Base64_URLDecode" | |||
!macro Base64_Decode _encodedtext | |||
push `${_encodedtext}` | |||
push `${BASE64_ENCODINGTABLE}` | |||
Call Base64_Decode | |||
!macroend | |||
!macro Base64_URLDecode _encodedtext | |||
push `${_encodedtext}` | |||
push `${BASE64_ENCODINGTABLEURL}` | |||
Call Base64_Decode | |||
!macroend | |||
Function | Function base64_Decode | ||
; Stack: strBase64table strEncoded | |||
Push $9 ; Stack: $9 strBase64table strEncoded ; $9 = strDecoded | |||
Exch 2 ; Stack: strEncoded strBase64table $9 | |||
Exch ; Stack: strBase64table strEncoded $9 | |||
Exch $0 ; Stack: $0 strEncoded $9 ; $0 = strBase64table | |||
Exch ; Stack: strEncoded $0 $9 | |||
Exch $1 ; Stack: $1 $0 $9 ; $1 = strEncoded | |||
Push $2 ; strBase64table.length | |||
Push $3 ; strEncoded.length | |||
Push $4 ; strBase64table.counter | |||
Push $5 ; strEncoded.counter | |||
Push $6 ; strBase64table.char | |||
Push $7 ; strEncoded.char | |||
Push $R0 ; 6bit-group.counter | |||
Push $R1 ; 6bit-group.a | |||
Push $R2 ; 6bit-group.b | |||
Push $R3 ; 6bit-group.c | |||
Push $R4 ; 6bit-group.d | |||
Push $R5 ; bit-group.tempVar.a | |||
Push $R6 ; bit-group.tempVar.b | |||
Push $R7 ; 8bit-group.A | |||
Push $R8 ; 8bit-group.B | |||
Push $R9 ; 8bit-group.C | |||
StrCpy $9 "" ; Result string | |||
StrLen $2 "$0" ; Get the length of the base64 table into $2 | |||
StrLen $3 "$1" ; Get the length of the encoded text into $3 | |||
IntOp $3 $3 - 1 ; Subtract one as the StrCpy offset is zero-based | |||
StrCpy $R0 4 ; Initialize the 6bit-group.counter | |||
${ForEach} $5 0 $3 + 1 ; Loop over the encoded string | |||
StrCpy $7 $1 1 $5 ; Grab the character at the loop counter's index | |||
${If} $7 == "${BASE64_PADDING}" ; If it's the padding char | |||
Push 0 ; Push value 0 (no impact on decoded string) | |||
${Else} ; Otherwise | |||
${ForEach} $4 0 $2 + 1 ; Loop over the base64 lookup table | |||
StrCpy $6 $0 1 $4 ; Grab the character at this loop counter's index | |||
${If} $6 S== $7 ; If that character matches the encoded string character | |||
${ExitFor} ; Exit this loop early | |||
${EndIf} | |||
${Next} | |||
Push $4 ; Push the lookup's index to the stack | |||
${EndIf} | |||
IntOp $R0 $R0 - 1 ; Decrease the 6bit-group counter | |||
${If} $R0 = 0 ; If that counter reaches zero | |||
; Pop the index values off the stack to variables | |||
Pop $R4 | |||
Pop $R3 | |||
Pop $R2 | |||
Pop $R1 | |||
; The way the base64 decoding works is like this... | |||
; Normal ASCII has 8 bits, base64 has 6 bits. | |||
; Those 8 bits need to be presented as 6 bits somehow | |||
; Turns out you can easily do that by taking their common multiple: 24 | |||
; This results in 3 8bit characters per each 4 6bit characters: | |||
; AAAAAAAA BBBBBBBB CCCCCCCC | |||
; aaaaaabb bbbbcccc ccdddddd | |||
; So to go back to AAAAAAAA, you need: | |||
; aaaaaa shifted two bits to the left | |||
; the two left-most bits of bbbbbb, | |||
; which you can do by shifting it four bits to the right | |||
IntOp $R5 $R1 << 2 | |||
IntOp $R6 $R2 >> 4 | |||
IntOp $R5 $R5 | $R6 | |||
IntFmt $R7 "%c" $R5 ; IntFmt turns the resulting 8bit value to a character | |||
; For BBBBBBBB, you need: | |||
; the four least significant bits of bbbbbb | |||
; which you can get by binary OR'ing with 2^4-1 = 15 | |||
; the four most significant bits of cccccc | |||
; which you can get by just shifting it two bits to the right | |||
IntOp $R5 $R2 & 15 | |||
InTop $R5 $R5 << 4 | |||
IntOp $R6 $R3 >> 2 | |||
IntOp $R5 $R5 | $R6 | |||
IntFmt $R8 "%c" $R5 | |||
; For CCCCCCCC, the procedure is entirely similar. | |||
IntOp $R5 $R3 & 3 | |||
IntOp $R5 $R5 << 6 | |||
IntOp $R5 $R5 | $R4 | |||
IntFmt $R9 "%c" $R5 | |||
StrCpy $9 "$9$R7$R8$R9" ; Tack it all onto the result | |||
StrCpy $R0 4 ; Reset the 6bit-group counter | |||
${EndIf} | |||
${Next} | |||
; Done. Now let's restore the user's variables | |||
Pop $R9 | |||
Pop $R8 | |||
Pop $R7 | |||
Pop $R6 | |||
Pop $R5 | |||
Pop $R4 | |||
Pop $R3 | |||
Pop $R2 | |||
Pop $R1 | |||
Pop $R0 | |||
Pop $7 | |||
Pop $6 | |||
Pop $5 | |||
Pop $4 | |||
Pop $3 | |||
Pop $2 | |||
Pop $1 | |||
Pop $0 | |||
Exch $9 ; Stack: strDecoded | |||
FunctionEnd | FunctionEnd | ||
!endif ;BASE64_NSH | !endif ;BASE64_NSH | ||
</highlight-nsis> |
Latest revision as of 01:01, 25 September 2010
BASE64 Encoding/Decoding Functions
As a result of needing to send data in a URL from my NSIS Installer, and because I didn't want to include another dependency, 99999999 wrote the below Base64 Encoder.
Please note: this header depends on the CharToASCII header.
Forum thread: http://forums.winamp.com/showthread.php?t=297879 "Base64 Encoder in NSIS"
Usage example as follows:
!include "CharToASCII.nsh" !include "Base64.nsh" OutFile "$%temp%\temp.exe" Section ${Base64_Encode} "THIS WILL BE ENCODED" Pop $0 ; $0 now equals VEhJUyBXSUxMIEJFIEVOQ09ERUQ= MessageBox MB_OK "$0" SectionEnd
Also there is a variant for encoding in a URL safe form, ${Base64_URLEncode}, which just uses a different encoding table, (but a standard one,) from the original version. I don't know how this works with Binary Data, but it works great with string data, which you need to put into a consistent form.
Animaether added decoding routines.
Forum thread: http://forums.winamp.com/showthread.php?t=322673 "Base64 decoder"
!include "CharToASCII.nsh" !include "Base64.nsh" OutFile "$%temp%\temp.exe" Section ${Base64_Decode} "VEhJUyBXSUxMIEJFIERFQ09ERUQ=" Pop $0 ; $0 now equals "THIS WILL BE DECODED" MessageBox MB_OK "$0" SectionEnd
As with the encoding functions, a URL safe form exists in the form of ${Base64_URLDecode}.
The header code
!ifndef BASE64_NSH !define BASE64_NSH !define BASE64_ENCODINGTABLE "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" !define BASE64_ENCODINGTABLEURL "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" !define BASE64_PADDING "=" VAR OCTETVALUE VAR BASE64TEMP !define Base64_Encode "!insertmacro Base64_Encode" !define Base64_URLEncode "!insertmacro Base64_URLEncode" !macro Base64_Encode _cleartext push $R0 push $R1 push $R2 push $0 push $1 push $2 push $3 push $4 push $5 push $6 push $7 push `${_cleartext}` push `${BASE64_ENCODINGTABLE}` Call Base64_Encode Pop $BASE64TEMP Pop $7 Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 pop $R2 pop $R1 pop $R0 Push $BASE64TEMP !macroend !macro Base64_URLEncode _cleartext push $R0 push $R1 push $R2 push $0 push $1 push $2 push $3 push $4 push $5 push $6 push $7 push `${_cleartext}` push `${BASE64_ENCODINGTABLEURL}` Call Base64_Encode Pop $BASE64TEMP Pop $7 Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 pop $R2 pop $R1 pop $R0 Push $BASE64TEMP !macroend Function Base64_Encode pop $R2 ; Encoding table pop $R0 ; Clear Text StrCpy "$R1" "" # The result StrLen $1 "$R0" StrCpy $0 0 ${WHILE} $0 < $1 # Copy 3 characters, and for each character push their value. StrCpy $OCTETVALUE 0 StrCpy $5 $0 StrCpy $4 "$R0" 1 $5 ${CharToASCII} $4 "$4" IntOp $OCTETVALUE $4 << 16 IntOp $5 $5 + 1 ${IF} $5 < $1 StrCpy $4 "$R0" 1 $5 ${CharToASCII} $4 "$4" IntOp $4 $4 << 8 IntOp $OCTETVALUE $OCTETVALUE + $4 IntOp $5 $5 + 1 ${IF} $5 < $1 StrCpy $4 "$R0" 1 $5 ${CharToASCII} $4 "$4" IntOp $OCTETVALUE $OCTETVALUE + $4 ${ENDIF} ${ENDIF} # Now take the 4 indexes from the encoding table, based on 6bits each of the octet's value. IntOp $4 $OCTETVALUE >> 18 IntOp $4 $4 & 63 StrCpy $5 "$R2" 1 $4 StrCpy $R1 "$R1$5" IntOp $4 $OCTETVALUE >> 12 IntOp $4 $4 & 63 StrCpy $5 "$R2" 1 $4 StrCpy $R1 "$R1$5" StrCpy $6 $0 StrCpy $7 2 IntOp $6 $6 + 1 ${IF} $6 < $1 IntOp $4 $OCTETVALUE >> 6 IntOp $4 $4 & 63 StrCpy $5 "$R2" 1 $4 StrCpy $R1 "$R1$5" IntOp $7 $7 - 1 ${ENDIF} IntOp $6 $6 + 1 ${IF} $6 < $1 IntOp $4 $OCTETVALUE & 63 StrCpy $5 "$R2" 1 $4 StrCpy $R1 "$R1$5" IntOp $7 $7 - 1 ${ENDIF} # If there is any padding required, we now write that here. ${IF} $7 > 0 ${WHILE} $7 > 0 StrCpy $R1 "$R1${BASE64_PADDING}" IntOp $7 $7 - 1 ${ENDWHILE} ${ENDIF} IntOp $0 $0 + 3 ${ENDWHILE} Push "$R1" FunctionEnd !define Base64_Decode "!insertmacro Base64_Decode" !define Base64_URLDecode "!insertmacro Base64_URLDecode" !macro Base64_Decode _encodedtext push `${_encodedtext}` push `${BASE64_ENCODINGTABLE}` Call Base64_Decode !macroend !macro Base64_URLDecode _encodedtext push `${_encodedtext}` push `${BASE64_ENCODINGTABLEURL}` Call Base64_Decode !macroend Function base64_Decode ; Stack: strBase64table strEncoded Push $9 ; Stack: $9 strBase64table strEncoded ; $9 = strDecoded Exch 2 ; Stack: strEncoded strBase64table $9 Exch ; Stack: strBase64table strEncoded $9 Exch $0 ; Stack: $0 strEncoded $9 ; $0 = strBase64table Exch ; Stack: strEncoded $0 $9 Exch $1 ; Stack: $1 $0 $9 ; $1 = strEncoded Push $2 ; strBase64table.length Push $3 ; strEncoded.length Push $4 ; strBase64table.counter Push $5 ; strEncoded.counter Push $6 ; strBase64table.char Push $7 ; strEncoded.char Push $R0 ; 6bit-group.counter Push $R1 ; 6bit-group.a Push $R2 ; 6bit-group.b Push $R3 ; 6bit-group.c Push $R4 ; 6bit-group.d Push $R5 ; bit-group.tempVar.a Push $R6 ; bit-group.tempVar.b Push $R7 ; 8bit-group.A Push $R8 ; 8bit-group.B Push $R9 ; 8bit-group.C StrCpy $9 "" ; Result string StrLen $2 "$0" ; Get the length of the base64 table into $2 StrLen $3 "$1" ; Get the length of the encoded text into $3 IntOp $3 $3 - 1 ; Subtract one as the StrCpy offset is zero-based StrCpy $R0 4 ; Initialize the 6bit-group.counter ${ForEach} $5 0 $3 + 1 ; Loop over the encoded string StrCpy $7 $1 1 $5 ; Grab the character at the loop counter's index ${If} $7 == "${BASE64_PADDING}" ; If it's the padding char Push 0 ; Push value 0 (no impact on decoded string) ${Else} ; Otherwise ${ForEach} $4 0 $2 + 1 ; Loop over the base64 lookup table StrCpy $6 $0 1 $4 ; Grab the character at this loop counter's index ${If} $6 S== $7 ; If that character matches the encoded string character ${ExitFor} ; Exit this loop early ${EndIf} ${Next} Push $4 ; Push the lookup's index to the stack ${EndIf} IntOp $R0 $R0 - 1 ; Decrease the 6bit-group counter ${If} $R0 = 0 ; If that counter reaches zero ; Pop the index values off the stack to variables Pop $R4 Pop $R3 Pop $R2 Pop $R1 ; The way the base64 decoding works is like this... ; Normal ASCII has 8 bits, base64 has 6 bits. ; Those 8 bits need to be presented as 6 bits somehow ; Turns out you can easily do that by taking their common multiple: 24 ; This results in 3 8bit characters per each 4 6bit characters: ; AAAAAAAA BBBBBBBB CCCCCCCC ; aaaaaabb bbbbcccc ccdddddd ; So to go back to AAAAAAAA, you need: ; aaaaaa shifted two bits to the left ; the two left-most bits of bbbbbb, ; which you can do by shifting it four bits to the right IntOp $R5 $R1 << 2 IntOp $R6 $R2 >> 4 IntOp $R5 $R5 | $R6 IntFmt $R7 "%c" $R5 ; IntFmt turns the resulting 8bit value to a character ; For BBBBBBBB, you need: ; the four least significant bits of bbbbbb ; which you can get by binary OR'ing with 2^4-1 = 15 ; the four most significant bits of cccccc ; which you can get by just shifting it two bits to the right IntOp $R5 $R2 & 15 InTop $R5 $R5 << 4 IntOp $R6 $R3 >> 2 IntOp $R5 $R5 | $R6 IntFmt $R8 "%c" $R5 ; For CCCCCCCC, the procedure is entirely similar. IntOp $R5 $R3 & 3 IntOp $R5 $R5 << 6 IntOp $R5 $R5 | $R4 IntFmt $R9 "%c" $R5 StrCpy $9 "$9$R7$R8$R9" ; Tack it all onto the result StrCpy $R0 4 ; Reset the 6bit-group counter ${EndIf} ${Next} ; Done. Now let's restore the user's variables Pop $R9 Pop $R8 Pop $R7 Pop $R6 Pop $R5 Pop $R4 Pop $R3 Pop $R2 Pop $R1 Pop $R0 Pop $7 Pop $6 Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0 Exch $9 ; Stack: strDecoded FunctionEnd !endif ;BASE64_NSH