Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 940b6b5d841c752e2be6a563c614d4a2af435edd (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
/*******************************************************************************
 * Copyright (c) 2006, 2012 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.osgi.internal.signedcontent;

public class Base64 {

	private static final byte equalSign = (byte) '=';

	static char digits[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', //
			'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', //
			'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', //
			'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'};

	/**
	 * This method decodes the byte array in base 64 encoding into a char array
	 * Base 64 encoding has to be according to the specification given by the
	 * RFC 1521 (5.2).
	 *
	 * @param data the encoded byte array
	 * @return the decoded byte array
	 */
	public static byte[] decode(byte[] data) {
		if (data.length == 0)
			return data;
		int lastRealDataIndex = data.length - 1;
		while (data[lastRealDataIndex] == equalSign)
			lastRealDataIndex--;
		// original data digit is 8 bits long, but base64 digit is 6 bits long
		int padBytes = data.length - 1 - lastRealDataIndex;
		int byteLength = data.length * 6 / 8 - padBytes;
		byte[] result = new byte[byteLength];
		// Each 4 bytes of input (encoded) we end up with 3 bytes of output
		int dataIndex = 0;
		int resultIndex = 0;
		int allBits = 0;
		// how many result chunks we can process before getting to pad bytes
		int resultChunks = (lastRealDataIndex + 1) / 4;
		for (int i = 0; i < resultChunks; i++) {
			allBits = 0;
			// Loop 4 times gathering input bits (4 * 6 = 24)
			for (int j = 0; j < 4; j++)
				allBits = (allBits << 6) | decodeDigit(data[dataIndex++]);
			// Loop 3 times generating output bits (3 * 8 = 24)
			for (int j = resultIndex + 2; j >= resultIndex; j--) {
				result[j] = (byte) (allBits & 0xff); // Bottom 8 bits
				allBits = allBits >>> 8;
			}
			resultIndex += 3; // processed 3 result bytes
		}
		// Now we do the extra bytes in case the original (non-encoded) data
		// was not multiple of 3 bytes
		switch (padBytes) {
			case 1 :
				// 1 pad byte means 3 (4-1) extra Base64 bytes of input, 18
				// bits, of which only 16 are meaningful
				// Or: 2 bytes of result data
				allBits = 0;
				// Loop 3 times gathering input bits
				for (int j = 0; j < 3; j++)
					allBits = (allBits << 6) | decodeDigit(data[dataIndex++]);
				// NOTE - The code below ends up being equivalent to allBits =
				// allBits>>>2
				// But we code it in a non-optimized way for clarity
				// The 4th, missing 6 bits are all 0
				allBits = allBits << 6;
				// The 3rd, missing 8 bits are all 0
				allBits = allBits >>> 8;
				// Loop 2 times generating output bits
				for (int j = resultIndex + 1; j >= resultIndex; j--) {
					result[j] = (byte) (allBits & 0xff); // Bottom 8
					// bits
					allBits = allBits >>> 8;
				}
				break;
			case 2 :
				// 2 pad bytes mean 2 (4-2) extra Base64 bytes of input, 12 bits
				// of data, of which only 8 are meaningful
				// Or: 1 byte of result data
				allBits = 0;
				// Loop 2 times gathering input bits
				for (int j = 0; j < 2; j++)
					allBits = (allBits << 6) | decodeDigit(data[dataIndex++]);
				// NOTE - The code below ends up being equivalent to allBits =
				// allBits>>>4
				// But we code it in a non-optimized way for clarity
				// The 3rd and 4th, missing 6 bits are all 0
				allBits = allBits << 6;
				allBits = allBits << 6;
				// The 3rd and 4th, missing 8 bits are all 0
				allBits = allBits >>> 8;
				allBits = allBits >>> 8;
				result[resultIndex] = (byte) (allBits & 0xff); // Bottom
				// 8
				// bits
				break;
		}
		return result;
	}

	/**
	 * This method converts a Base 64 digit to its numeric value.
	 *
	 * @param data digit (character) to convert
	 * @return value for the digit
	 */
	static int decodeDigit(byte data) {
		char charData = (char) data;
		if (charData <= 'Z' && charData >= 'A')
			return charData - 'A';
		if (charData <= 'z' && charData >= 'a')
			return charData - 'a' + 26;
		if (charData <= '9' && charData >= '0')
			return charData - '0' + 52;
		switch (charData) {
			case '+' :
				return 62;
			case '/' :
				return 63;
			default :
				throw new IllegalArgumentException("Invalid char to decode: " + data); //$NON-NLS-1$
		}
	}

	/**
	 * This method encodes the byte array into a char array in base 64 according
	 * to the specification given by the RFC 1521 (5.2).
	 *
	 * @param data the encoded char array
	 * @return the byte array that needs to be encoded
	 */
	public static byte[] encode(byte[] data) {
		int sourceChunks = data.length / 3;
		int len = ((data.length + 2) / 3) * 4;
		byte[] result = new byte[len];
		int extraBytes = data.length - (sourceChunks * 3);
		// Each 4 bytes of input (encoded) we end up with 3 bytes of output
		int dataIndex = 0;
		int resultIndex = 0;
		int allBits = 0;
		for (int i = 0; i < sourceChunks; i++) {
			allBits = 0;
			// Loop 3 times gathering input bits (3 * 8 = 24)
			for (int j = 0; j < 3; j++)
				allBits = (allBits << 8) | (data[dataIndex++] & 0xff);
			// Loop 4 times generating output bits (4 * 6 = 24)
			for (int j = resultIndex + 3; j >= resultIndex; j--) {
				result[j] = (byte) digits[(allBits & 0x3f)]; // Bottom
				// 6
				// bits
				allBits = allBits >>> 6;
			}
			resultIndex += 4; // processed 4 result bytes
		}
		// Now we do the extra bytes in case the original (non-encoded) data
		// is not multiple of 4 bytes
		switch (extraBytes) {
			case 1 :
				allBits = data[dataIndex++]; // actual byte
				allBits = allBits << 8; // 8 bits of zeroes
				allBits = allBits << 8; // 8 bits of zeroes
				// Loop 4 times generating output bits (4 * 6 = 24)
				for (int j = resultIndex + 3; j >= resultIndex; j--) {
					result[j] = (byte) digits[(allBits & 0x3f)]; // Bottom
					// 6
					// bits
					allBits = allBits >>> 6;
				}
				// 2 pad tags
				result[result.length - 1] = (byte) '=';
				result[result.length - 2] = (byte) '=';
				break;
			case 2 :
				allBits = data[dataIndex++]; // actual byte
				allBits = (allBits << 8) | (data[dataIndex++] & 0xff); // actual
				// byte
				allBits = allBits << 8; // 8 bits of zeroes
				// Loop 4 times generating output bits (4 * 6 = 24)
				for (int j = resultIndex + 3; j >= resultIndex; j--) {
					result[j] = (byte) digits[(allBits & 0x3f)]; // Bottom
					// 6
					// bits
					allBits = allBits >>> 6;
				}
				// 1 pad tag
				result[result.length - 1] = (byte) '=';
				break;
		}
		return result;
	}
}

Back to the top