001/*
002 * The contents of this file are subject to the terms of the Common Development and
003 * Distribution License (the License). You may not use this file except in compliance with the
004 * License.
005 *
006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the
007 * specific language governing permission and limitations under the License.
008 *
009 * When distributing Covered Software, include this CDDL Header Notice in each file and include
010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL
011 * Header, with the fields enclosed by brackets [] replaced by your own identifying
012 * information: "Portions Copyright [year] [name of copyright owner]".
013 *
014 * Copyright 2006-2009 Sun Microsystems, Inc.
015 * Portions copyright 2012-2016 ForgeRock AS.
016 */
017package org.forgerock.opendj.ldap;
018
019import org.forgerock.util.Reject;
020import static com.forgerock.opendj.ldap.CoreMessages.ERR_BASE64_DECODE_INVALID_CHARACTER;
021import static com.forgerock.opendj.ldap.CoreMessages.ERR_BASE64_DECODE_INVALID_LENGTH;
022
023import org.forgerock.i18n.LocalizableMessage;
024import org.forgerock.i18n.LocalizedIllegalArgumentException;
025
026/**
027 * This class provides methods for performing base64 encoding and decoding.
028 * Base64 is a mechanism for encoding binary data in ASCII form by converting
029 * sets of three bytes with eight significant bits each to sets of four bytes
030 * with six significant bits each.
031 */
032public final class Base64 {
033    /**
034     * The set of characters that may be used in base64-encoded values.
035     */
036    private static final char[] BASE64_ALPHABET = ("ABCDEFGHIJKLMNOPQRSTUVWXYZ"
037            + "abcdefghijklmnopqrstuvwxyz" + "0123456789+/").toCharArray();
038
039    /**
040     * Decodes the provided base64 encoded data.
041     *
042     * @param base64
043     *            The base64 encoded data.
044     * @return The decoded data.
045     * @throws LocalizedIllegalArgumentException
046     *             If a problem occurs while attempting to decode {@code base64}
047     *             .
048     * @throws NullPointerException
049     *             If {@code base64} was {@code null}.
050     */
051    public static ByteString decode(final String base64) {
052        Reject.ifNull(base64);
053
054        // The encoded value must have length that is a multiple of four
055        // bytes.
056        final int length = base64.length();
057        if (length % 4 != 0) {
058            final LocalizableMessage message = ERR_BASE64_DECODE_INVALID_LENGTH.get(base64);
059            throw new LocalizedIllegalArgumentException(message);
060        }
061
062        final ByteStringBuilder builder = new ByteStringBuilder(length);
063        for (int i = 0; i < length; i += 4) {
064            boolean append = true;
065            int value = 0;
066
067            for (int j = 0; j < 4; j++) {
068                switch (base64.charAt(i + j)) {
069                case 'A':
070                    value <<= 6;
071                    break;
072                case 'B':
073                    value = (value << 6) | 0x01;
074                    break;
075                case 'C':
076                    value = (value << 6) | 0x02;
077                    break;
078                case 'D':
079                    value = (value << 6) | 0x03;
080                    break;
081                case 'E':
082                    value = (value << 6) | 0x04;
083                    break;
084                case 'F':
085                    value = (value << 6) | 0x05;
086                    break;
087                case 'G':
088                    value = (value << 6) | 0x06;
089                    break;
090                case 'H':
091                    value = (value << 6) | 0x07;
092                    break;
093                case 'I':
094                    value = (value << 6) | 0x08;
095                    break;
096                case 'J':
097                    value = (value << 6) | 0x09;
098                    break;
099                case 'K':
100                    value = (value << 6) | 0x0A;
101                    break;
102                case 'L':
103                    value = (value << 6) | 0x0B;
104                    break;
105                case 'M':
106                    value = (value << 6) | 0x0C;
107                    break;
108                case 'N':
109                    value = (value << 6) | 0x0D;
110                    break;
111                case 'O':
112                    value = (value << 6) | 0x0E;
113                    break;
114                case 'P':
115                    value = (value << 6) | 0x0F;
116                    break;
117                case 'Q':
118                    value = (value << 6) | 0x10;
119                    break;
120                case 'R':
121                    value = (value << 6) | 0x11;
122                    break;
123                case 'S':
124                    value = (value << 6) | 0x12;
125                    break;
126                case 'T':
127                    value = (value << 6) | 0x13;
128                    break;
129                case 'U':
130                    value = (value << 6) | 0x14;
131                    break;
132                case 'V':
133                    value = (value << 6) | 0x15;
134                    break;
135                case 'W':
136                    value = (value << 6) | 0x16;
137                    break;
138                case 'X':
139                    value = (value << 6) | 0x17;
140                    break;
141                case 'Y':
142                    value = (value << 6) | 0x18;
143                    break;
144                case 'Z':
145                    value = (value << 6) | 0x19;
146                    break;
147                case 'a':
148                    value = (value << 6) | 0x1A;
149                    break;
150                case 'b':
151                    value = (value << 6) | 0x1B;
152                    break;
153                case 'c':
154                    value = (value << 6) | 0x1C;
155                    break;
156                case 'd':
157                    value = (value << 6) | 0x1D;
158                    break;
159                case 'e':
160                    value = (value << 6) | 0x1E;
161                    break;
162                case 'f':
163                    value = (value << 6) | 0x1F;
164                    break;
165                case 'g':
166                    value = (value << 6) | 0x20;
167                    break;
168                case 'h':
169                    value = (value << 6) | 0x21;
170                    break;
171                case 'i':
172                    value = (value << 6) | 0x22;
173                    break;
174                case 'j':
175                    value = (value << 6) | 0x23;
176                    break;
177                case 'k':
178                    value = (value << 6) | 0x24;
179                    break;
180                case 'l':
181                    value = (value << 6) | 0x25;
182                    break;
183                case 'm':
184                    value = (value << 6) | 0x26;
185                    break;
186                case 'n':
187                    value = (value << 6) | 0x27;
188                    break;
189                case 'o':
190                    value = (value << 6) | 0x28;
191                    break;
192                case 'p':
193                    value = (value << 6) | 0x29;
194                    break;
195                case 'q':
196                    value = (value << 6) | 0x2A;
197                    break;
198                case 'r':
199                    value = (value << 6) | 0x2B;
200                    break;
201                case 's':
202                    value = (value << 6) | 0x2C;
203                    break;
204                case 't':
205                    value = (value << 6) | 0x2D;
206                    break;
207                case 'u':
208                    value = (value << 6) | 0x2E;
209                    break;
210                case 'v':
211                    value = (value << 6) | 0x2F;
212                    break;
213                case 'w':
214                    value = (value << 6) | 0x30;
215                    break;
216                case 'x':
217                    value = (value << 6) | 0x31;
218                    break;
219                case 'y':
220                    value = (value << 6) | 0x32;
221                    break;
222                case 'z':
223                    value = (value << 6) | 0x33;
224                    break;
225                case '0':
226                    value = (value << 6) | 0x34;
227                    break;
228                case '1':
229                    value = (value << 6) | 0x35;
230                    break;
231                case '2':
232                    value = (value << 6) | 0x36;
233                    break;
234                case '3':
235                    value = (value << 6) | 0x37;
236                    break;
237                case '4':
238                    value = (value << 6) | 0x38;
239                    break;
240                case '5':
241                    value = (value << 6) | 0x39;
242                    break;
243                case '6':
244                    value = (value << 6) | 0x3A;
245                    break;
246                case '7':
247                    value = (value << 6) | 0x3B;
248                    break;
249                case '8':
250                    value = (value << 6) | 0x3C;
251                    break;
252                case '9':
253                    value = (value << 6) | 0x3D;
254                    break;
255                case '+':
256                    value = (value << 6) | 0x3E;
257                    break;
258                case '/':
259                    value = (value << 6) | 0x3F;
260                    break;
261                case '=':
262                    append = false;
263                    switch (j) {
264                    case 2:
265                        builder.appendByte(value >>> 4);
266                        break;
267                    case 3:
268                        builder.appendByte(value >>> 10);
269                        builder.appendByte(value >>> 2);
270                        break;
271                    }
272                    break;
273                default:
274                    final LocalizableMessage message =
275                            ERR_BASE64_DECODE_INVALID_CHARACTER.get(base64, base64.charAt(i + j));
276                    throw new LocalizedIllegalArgumentException(message);
277                }
278
279                if (!append) {
280                    break;
281                }
282            }
283
284            if (append) {
285                builder.appendByte(value >>> 16);
286                builder.appendByte(value >>> 8);
287                builder.appendByte(value);
288            } else {
289                break;
290            }
291        }
292
293        return builder.toByteString();
294    }
295
296    /**
297     * Encodes the provided data as a base64 string.
298     *
299     * @param bytes
300     *            The data to be encoded.
301     * @return The base64 encoded representation of {@code bytes}.
302     * @throws NullPointerException
303     *             If {@code bytes} was {@code null}.
304     */
305    public static String encode(final byte[] bytes) {
306        return encode(ByteString.wrap(bytes));
307    }
308
309
310    /**
311     * Encodes the provided data as a base64 string.
312     *
313     * @param bytes
314     *            The data to be encoded.
315     * @return The base64 encoded representation of {@code bytes}.
316     * @throws NullPointerException
317     *             If {@code bytes} was {@code null}.
318     */
319    public static String encode(final ByteSequence bytes) {
320        Reject.ifNull(bytes);
321
322        if (bytes.isEmpty()) {
323            return "";
324        }
325
326        final StringBuilder buffer = new StringBuilder(4 * bytes.length() / 3);
327
328        int pos = 0;
329        final int iterations = bytes.length() / 3;
330        for (int i = 0; i < iterations; i++) {
331            final int value =
332                    ((bytes.byteAt(pos++) & 0xFF) << 16) | ((bytes.byteAt(pos++) & 0xFF) << 8)
333                            | (bytes.byteAt(pos++) & 0xFF);
334
335            buffer.append(BASE64_ALPHABET[(value >>> 18) & 0x3F]);
336            buffer.append(BASE64_ALPHABET[(value >>> 12) & 0x3F]);
337            buffer.append(BASE64_ALPHABET[(value >>> 6) & 0x3F]);
338            buffer.append(BASE64_ALPHABET[value & 0x3F]);
339        }
340
341        switch (bytes.length() % 3) {
342        case 1:
343            buffer.append(BASE64_ALPHABET[(bytes.byteAt(pos) >>> 2) & 0x3F]);
344            buffer.append(BASE64_ALPHABET[(bytes.byteAt(pos) << 4) & 0x3F]);
345            buffer.append("==");
346            break;
347        case 2:
348            final int value = ((bytes.byteAt(pos++) & 0xFF) << 8) | (bytes.byteAt(pos) & 0xFF);
349            buffer.append(BASE64_ALPHABET[(value >>> 10) & 0x3F]);
350            buffer.append(BASE64_ALPHABET[(value >>> 4) & 0x3F]);
351            buffer.append(BASE64_ALPHABET[(value << 2) & 0x3F]);
352            buffer.append("=");
353            break;
354        }
355
356        return buffer.toString();
357    }
358
359    /**
360     * Prevent instance creation.
361     */
362    private Base64() {
363        // No implementation required.
364    }
365}