View Javadoc
1   //
2   //  NOTE: The following source code is heavily derived from the
3   //  iHarder.net public domain Base64 library.  See the original at
4   //  http://iharder.sourceforge.net/current/java/base64/
5   //
6   
7   package org.eclipse.jgit.util;
8   
9   import java.io.UnsupportedEncodingException;
10  import java.nio.charset.StandardCharsets;
11  import java.text.MessageFormat;
12  import java.util.Arrays;
13  
14  import org.eclipse.jgit.internal.JGitText;
15  
16  /**
17   * Encodes and decodes to and from Base64 notation.
18   * <p>
19   * I am placing this code in the Public Domain. Do with it as you will. This
20   * software comes with no guarantees or warranties but with plenty of
21   * well-wishing instead! Please visit <a
22   * href="http://iharder.net/base64">http://iharder.net/base64</a> periodically
23   * to check for updates or to contribute improvements.
24   * </p>
25   *
26   * @author Robert Harder
27   * @author rob@iharder.net
28   * @version 2.1, stripped to minimum feature set used by JGit.
29   */
30  public class Base64 {
31  	/** The equals sign (=) as a byte. */
32  	private final static byte EQUALS_SIGN = (byte) '=';
33  
34  	/** Indicates equals sign in encoding. */
35  	private final static byte EQUALS_SIGN_DEC = -1;
36  
37  	/** Indicates white space in encoding. */
38  	private final static byte WHITE_SPACE_DEC = -2;
39  
40  	/** Indicates an invalid byte during decoding. */
41  	private final static byte INVALID_DEC = -3;
42  
43  	/** Preferred encoding. */
44  	private final static String UTF_8 = "UTF-8"; //$NON-NLS-1$
45  
46  	/** The 64 valid Base64 values. */
47  	private final static byte[] ENC;
48  
49  	/**
50  	 * Translates a Base64 value to either its 6-bit reconstruction value or a
51  	 * negative number indicating some other meaning. The table is only 7 bits
52  	 * wide, as the 8th bit is discarded during decoding.
53  	 */
54  	private final static byte[] DEC;
55  
56  	static {
57  		try {
58  			ENC = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ" // //$NON-NLS-1$
59  					+ "abcdefghijklmnopqrstuvwxyz" // //$NON-NLS-1$
60  					+ "0123456789" // //$NON-NLS-1$
61  					+ "+/" // //$NON-NLS-1$
62  			).getBytes(UTF_8);
63  		} catch (UnsupportedEncodingException uee) {
64  			throw new RuntimeException(uee.getMessage(), uee);
65  		}
66  
67  		DEC = new byte[128];
68  		Arrays.fill(DEC, INVALID_DEC);
69  
70  		for (int i = 0; i < 64; i++)
71  			DEC[ENC[i]] = (byte) i;
72  		DEC[EQUALS_SIGN] = EQUALS_SIGN_DEC;
73  
74  		DEC['\t'] = WHITE_SPACE_DEC;
75  		DEC['\n'] = WHITE_SPACE_DEC;
76  		DEC['\r'] = WHITE_SPACE_DEC;
77  		DEC[' '] = WHITE_SPACE_DEC;
78  	}
79  
80  	/** Defeats instantiation. */
81  	private Base64() {
82  		// Suppress empty block warning.
83  	}
84  
85  	/**
86  	 * Encodes up to three bytes of the array <var>source</var> and writes the
87  	 * resulting four Base64 bytes to <var>destination</var>. The source and
88  	 * destination arrays can be manipulated anywhere along their length by
89  	 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
90  	 * does not check to make sure your arrays are large enough to accommodate
91  	 * <var>srcOffset</var> + 3 for the <var>source</var> array or
92  	 * <var>destOffset</var> + 4 for the <var>destination</var> array. The
93  	 * actual number of significant bytes in your array is given by
94  	 * <var>numSigBytes</var>.
95  	 *
96  	 * @param source
97  	 *            the array to convert
98  	 * @param srcOffset
99  	 *            the index where conversion begins
100 	 * @param numSigBytes
101 	 *            the number of significant bytes in your array
102 	 * @param destination
103 	 *            the array to hold the conversion
104 	 * @param destOffset
105 	 *            the index where output will be put
106 	 */
107 	private static void encode3to4(byte[] source, int srcOffset,
108 			int numSigBytes, byte[] destination, int destOffset) {
109 		// We have to shift left 24 in order to flush out the 1's that appear
110 		// when Java treats a value as negative that is cast from a byte.
111 
112 		int inBuff = 0;
113 		switch (numSigBytes) {
114 		case 3:
115 			inBuff |= (source[srcOffset + 2] << 24) >>> 24;
116 			//$FALL-THROUGH$
117 
118 		case 2:
119 			inBuff |= (source[srcOffset + 1] << 24) >>> 16;
120 			//$FALL-THROUGH$
121 
122 		case 1:
123 			inBuff |= (source[srcOffset] << 24) >>> 8;
124 		}
125 
126 		switch (numSigBytes) {
127 		case 3:
128 			destination[destOffset] = ENC[(inBuff >>> 18)];
129 			destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
130 			destination[destOffset + 2] = ENC[(inBuff >>> 6) & 0x3f];
131 			destination[destOffset + 3] = ENC[(inBuff) & 0x3f];
132 			break;
133 
134 		case 2:
135 			destination[destOffset] = ENC[(inBuff >>> 18)];
136 			destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
137 			destination[destOffset + 2] = ENC[(inBuff >>> 6) & 0x3f];
138 			destination[destOffset + 3] = EQUALS_SIGN;
139 			break;
140 
141 		case 1:
142 			destination[destOffset] = ENC[(inBuff >>> 18)];
143 			destination[destOffset + 1] = ENC[(inBuff >>> 12) & 0x3f];
144 			destination[destOffset + 2] = EQUALS_SIGN;
145 			destination[destOffset + 3] = EQUALS_SIGN;
146 			break;
147 		}
148 	}
149 
150 	/**
151 	 * Encodes a byte array into Base64 notation.
152 	 *
153 	 * @param source
154 	 *            The data to convert
155 	 * @return encoded base64 representation of source.
156 	 */
157 	public static String encodeBytes(byte[] source) {
158 		return encodeBytes(source, 0, source.length);
159 	}
160 
161 	/**
162 	 * Encodes a byte array into Base64 notation.
163 	 *
164 	 * @param source
165 	 *            The data to convert
166 	 * @param off
167 	 *            Offset in array where conversion should begin
168 	 * @param len
169 	 *            Length of data to convert
170 	 * @return encoded base64 representation of source.
171 	 */
172 	public static String encodeBytes(byte[] source, int off, int len) {
173 		final int len43 = len * 4 / 3;
174 
175 		byte[] outBuff = new byte[len43 + ((len % 3) > 0 ? 4 : 0)];
176 		int d = 0;
177 		int e = 0;
178 		int len2 = len - 2;
179 
180 		for (; d < len2; d += 3, e += 4)
181 			encode3to4(source, d + off, 3, outBuff, e);
182 
183 		if (d < len) {
184 			encode3to4(source, d + off, len - d, outBuff, e);
185 			e += 4;
186 		}
187 
188 		return new String(outBuff, 0, e, StandardCharsets.UTF_8);
189 	}
190 
191 	/**
192 	 * Decodes four bytes from array <var>source</var> and writes the resulting
193 	 * bytes (up to three of them) to <var>destination</var>. The source and
194 	 * destination arrays can be manipulated anywhere along their length by
195 	 * specifying <var>srcOffset</var> and <var>destOffset</var>. This method
196 	 * does not check to make sure your arrays are large enough to accommodate
197 	 * <var>srcOffset</var> + 4 for the <var>source</var> array or
198 	 * <var>destOffset</var> + 3 for the <var>destination</var> array. This
199 	 * method returns the actual number of bytes that were converted from the
200 	 * Base64 encoding.
201 	 *
202 	 * @param source
203 	 *            the array to convert
204 	 * @param srcOffset
205 	 *            the index where conversion begins
206 	 * @param destination
207 	 *            the array to hold the conversion
208 	 * @param destOffset
209 	 *            the index where output will be put
210 	 * @return the number of decoded bytes converted
211 	 */
212 	private static int decode4to3(byte[] source, int srcOffset,
213 			byte[] destination, int destOffset) {
214 		// Example: Dk==
215 		if (source[srcOffset + 2] == EQUALS_SIGN) {
216 			int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
217 					| ((DEC[source[srcOffset + 1]] & 0xFF) << 12);
218 			destination[destOffset] = (byte) (outBuff >>> 16);
219 			return 1;
220 		}
221 
222 		// Example: DkL=
223 		else if (source[srcOffset + 3] == EQUALS_SIGN) {
224 			int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
225 					| ((DEC[source[srcOffset + 1]] & 0xFF) << 12)
226 					| ((DEC[source[srcOffset + 2]] & 0xFF) << 6);
227 			destination[destOffset] = (byte) (outBuff >>> 16);
228 			destination[destOffset + 1] = (byte) (outBuff >>> 8);
229 			return 2;
230 		}
231 
232 		// Example: DkLE
233 		else {
234 			int outBuff = ((DEC[source[srcOffset]] & 0xFF) << 18)
235 					| ((DEC[source[srcOffset + 1]] & 0xFF) << 12)
236 					| ((DEC[source[srcOffset + 2]] & 0xFF) << 6)
237 					| ((DEC[source[srcOffset + 3]] & 0xFF));
238 
239 			destination[destOffset] = (byte) (outBuff >> 16);
240 			destination[destOffset + 1] = (byte) (outBuff >> 8);
241 			destination[destOffset + 2] = (byte) (outBuff);
242 
243 			return 3;
244 		}
245 	}
246 
247 	/**
248 	 * Low-level decoding ASCII characters from a byte array.
249 	 *
250 	 * @param source
251 	 *            The Base64 encoded data
252 	 * @param off
253 	 *            The offset of where to begin decoding
254 	 * @param len
255 	 *            The length of characters to decode
256 	 * @return decoded data
257 	 * @throws IllegalArgumentException
258 	 *             the input is not a valid Base64 sequence.
259 	 */
260 	public static byte[] decode(byte[] source, int off, int len) {
261 		byte[] outBuff = new byte[len * 3 / 4]; // Upper limit on size of output
262 		int outBuffPosn = 0;
263 
264 		byte[] b4 = new byte[4];
265 		int b4Posn = 0;
266 
267 		for (int i = off; i < off + len; i++) {
268 			byte sbiCrop = (byte) (source[i] & 0x7f);
269 			byte sbiDecode = DEC[sbiCrop];
270 
271 			if (EQUALS_SIGN_DEC <= sbiDecode) {
272 				b4[b4Posn++] = sbiCrop;
273 				if (b4Posn > 3) {
274 					outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn);
275 					b4Posn = 0;
276 
277 					// If that was the equals sign, break out of 'for' loop
278 					if (sbiCrop == EQUALS_SIGN)
279 						break;
280 				}
281 
282 			} else if (sbiDecode != WHITE_SPACE_DEC)
283 				throw new IllegalArgumentException(MessageFormat.format(
284 						JGitText.get().badBase64InputCharacterAt,
285 						Integer.valueOf(i), Integer.valueOf(source[i] & 0xff)));
286 		}
287 
288 		if (outBuff.length == outBuffPosn)
289 			return outBuff;
290 
291 		byte[] out = new byte[outBuffPosn];
292 		System.arraycopy(outBuff, 0, out, 0, outBuffPosn);
293 		return out;
294 	}
295 
296 	/**
297 	 * Decodes data from Base64 notation.
298 	 *
299 	 * @param s
300 	 *            the string to decode
301 	 * @return the decoded data
302 	 */
303 	public static byte[] decode(String s) {
304 		byte[] bytes = s.getBytes(StandardCharsets.UTF_8);
305 		return decode(bytes, 0, bytes.length);
306 	}
307 }