View Javadoc

1   // Copyright (C) 2004, Brian Enigma <enigma at netninja.com>
2   // This file is part of MagicCodes.
3   //
4   // MagicCodes is free software; you can redistribute it and/or modify
5   // it under the terms of the GNU General Public License as published by
6   // the Free Software Foundation; either version 2 of the License, or
7   // (at your option) any later version.
8   //
9   // MagicCodes is distributed in the hope that it will be useful,
10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  // GNU General Public License for more details.
13  //
14  // You should have received a copy of the GNU General Public License
15  // along with Foobar; if not, write to the Free Software
16  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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 = '_';  // Start with a non-Base64 character
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         // Create a wrapper around the input to screen out non-Base64 characters
153         StringWrapper wrapper = new StringWrapper( in );
154         // A Base64 byte array is 75% the size of its String representation
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         // Continue until we have less than 4 full characters left to
163         // decode in the input.
164         while ( byteIndex + 2 < byteArrayLength ) {
165             
166             // Package a set of four characters into a byte triplet
167             // Each character contributes 6 bits of useful information
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             // Grab a normal byte (eight bits) out of the byte triplet
177             // and put it in the byte array
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         // Check if we have one byte left to decode
187         if ( byteIndex == byteArrayLength - 1 ) {
188             // Take out the last two characters from the String
189             byteTriplet = mapCharToInt( wrapper.getNextUsefulChar() );
190             byteTriplet <<= 6;
191             byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
192             
193             // Remove the padded zeros
194             byteTriplet >>= 4;
195             result[ byteIndex ] = (byte)( byteTriplet & EIGHT_BIT_MASK );
196         }
197 
198         // Check if we have two bytes left to decode
199         if ( byteIndex == byteArrayLength - 2 ) {
200             // Take out the last three characters from the String
201             byteTriplet = mapCharToInt( wrapper.getNextUsefulChar() );
202             byteTriplet <<= 6;
203             byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
204             byteTriplet <<= 6;
205             byteTriplet |= mapCharToInt( wrapper.getNextUsefulChar() );
206         
207             // Remove the padded zeros
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 }