1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.ninjasoft.magiccodes.plugins;
18
19
20 /***
21 * Decodes BASE-64 data
22 * @author enigma
23 */
24 public class Base64Decoder implements Plugin {
25
26 public String getName() {
27 return "BASE-64 Decoder";
28 }
29
30 public String getDescription() {
31 return "Decodes BASE-64 text to original data";
32 }
33
34 public boolean usesKey() {
35 return false;
36 }
37
38 public boolean isInformational() {
39 return false;
40 }
41
42 /***
43 * Convert a Base64 character to its 6 bit value as defined by the mapping.
44 * @param c Base64 character to decode
45 * @return int representation of 6 bit value
46 */
47 private int mapCharToInt( char c ) {
48 if ( c >= 'A' && c <= 'Z' ) {
49 return c - 'A';
50 }
51
52 if ( c >= 'a' && c <= 'z' ) {
53 return ( c - 'a' ) + Base64Encoder.LOWER_CASE_A_VALUE;
54 }
55
56 if ( c >= '0' && c <= '9' ) {
57 return ( c - '0' ) + Base64Encoder.ZERO_VALUE;
58 }
59
60 if ( c == '+' ) {
61 return Base64Encoder.PLUS_VALUE;
62 }
63
64 if ( c == '/' ) {
65 return Base64Encoder.SLASH_VALUE;
66 }
67
68 throw new IllegalArgumentException( c + " is not a valid Base64 character." );
69 }
70
71 /***
72 * Simple class to wrap around the String input to ignore all of the
73 * non-Base64 characters in the input. Note that although '=' is
74 * a valid character, it does not contribute to the total number
75 * of output bytes, and is therefore ignored
76 */
77 private class StringWrapper {
78
79 /***
80 * The input String to be decoded
81 */
82 private int[] mString;
83
84 /***
85 * Current position in the String
86 */
87 private int mIndex = 0;
88
89 /***
90 * Total number of Base64 characters in the input
91 */
92 private int mUsefulLength;
93
94 /***
95 * @param c Character to be examined
96 * @return Whether or not the character is a Base64 character
97 */
98 private boolean isUsefulChar( int i ) {
99 char c = (char) i;
100 return ( c >= 'A' && c <= 'Z' ) ||
101 ( c >= 'a' && c <= 'z' ) ||
102 ( c >= '0' && c <= '9' ) ||
103 ( c == '+' ) ||
104 ( c == '/' );
105 }
106
107 /***
108 * Create the wrapper and determine the number of Base64 characters in
109 * the input
110 * @param s Input String to be decoded
111 */
112 public StringWrapper( int[] in ) {
113 mString = in;
114 mUsefulLength = 0;
115 for( int i = 0; i < in.length; i++ ) {
116 if( isUsefulChar( mString[i] ) ){
117 mUsefulLength++;
118 }
119 }
120 }
121
122 /***
123 * @return Total number of Base64 characters in the input. Does
124 * not include '='
125 */
126 public int getUsefulLength() {
127 return mUsefulLength;
128 }
129
130 /***
131 * Traverse the String until hitting the next Base64 character.
132 * Assumes that there is still another valid Base64 character
133 * left in the String.
134 */
135 public char getNextUsefulChar() {
136 char result = '_';
137 while ( !isUsefulChar( result ) ) {
138 result = (char) mString[mIndex++];
139 }
140
141 return result;
142 }
143 }
144
145 /***
146 * Bit mask for one byte worth of bits in Base64 encoding.
147 * Equivalent to binary value 11111111b.
148 */
149 private static final int EIGHT_BIT_MASK = 0xFF;
150
151 public int[] doAction(int[] in, int[] key) {
152
153 StringWrapper wrapper = new StringWrapper( in );
154
155 int byteArrayLength = wrapper.getUsefulLength() * 3 / 4;
156
157 int result[] = new int[ byteArrayLength ];
158
159 int byteTriplet = 0;
160 int byteIndex = 0;
161
162
163
164 while ( byteIndex + 2 < byteArrayLength ) {
165
166
167
168 byteTriplet = mapCharToInt( wrapper.getNextUsefulChar() );
169 byteTriplet <<= 6;
170 byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
171 byteTriplet <<= 6;
172 byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
173 byteTriplet <<= 6;
174 byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
175
176
177
178 result[ byteIndex + 2 ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
179 byteTriplet >>= 8;
180 result[ byteIndex + 1 ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
181 byteTriplet >>= 8;
182 result[ byteIndex ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
183 byteIndex += 3;
184 }
185
186
187 if ( byteIndex == byteArrayLength - 1 ) {
188
189 byteTriplet = mapCharToInt( wrapper.getNextUsefulChar() );
190 byteTriplet <<= 6;
191 byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
192
193
194 byteTriplet >>= 4;
195 result[ byteIndex ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
196 }
197
198
199 if ( byteIndex == byteArrayLength - 2 ) {
200
201 byteTriplet = mapCharToInt( wrapper.getNextUsefulChar() );
202 byteTriplet <<= 6;
203 byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
204 byteTriplet <<= 6;
205 byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
206
207
208 byteTriplet >>= 2;
209 result[ byteIndex + 1 ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
210 byteTriplet >>= 8;
211 result[ byteIndex ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
212 }
213
214 return result;
215 }
216
217 }