    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.net.util;
    019
    020import java.math.BigInteger;
    021import java.nio.charset.StandardCharsets;
    022import java.util.Objects;
    023
    024/**
    025 * Provides Base64 encoding and decoding as defined by RFC 2045.
    026 *
    027 * <p>
    028 * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose Internet Mail Extensions (MIME) Part One:
    029 * Format of Internet Message Bodies</cite> by Freed and Borenstein.
    030 * </p>
    031 * <p>
    032 * The class can be parameterized in the following manner with various constructors:
    033 * <ul>
    034 * <li>URL-safe mode: Default off.</li>
    035 * <li>Line length: Default 76. Line length that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
    036 * <li>Line separator: Default is CRLF ("\r\n")</li>
    037 * </ul>
    038 * <p>
    039 * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode character encodings which are
    040 * compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc).
    041 * </p>
    042 *
    043 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a>
    044 * @since 2.2
    045 */
    046public class Base64 {
    047    private static final int DEFAULT_BUFFER_RESIZE_FACTOR = 2;
    048
    049    private static final int DEFAULT_BUFFER_SIZE = 8192;
    050
    051    /**
    052     * Chunk size per RFC 2045 section 6.8.
    053     *
    054     * <p>
    055     * The {@value} character limit does not count the trailing CRLF, but counts all other characters, including any equal signs.
    056     * </p>
    057     *
    058     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 6.8</a>
    059     */
    060    static final int CHUNK_SIZE = 76;
    061
    062    /**
    063     * Chunk separator per RFC 2045 section 2.1.
    064     *
    065     * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045 section 2.1</a>
    066     */
    067    private static final byte[] CHUNK_SEPARATOR = { '\r', '\n' };
    068
    069    /**
    070     * This array is a lookup table that translates 6-bit positive integer index values into their "Base64 Alphabet" equivalents as specified in Table 1 of RFC
    071     * 2045.
    072     *
    073     * Thanks to "commons" project in ws.apache.org for this code. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
    074     */
    075    private static final byte[] STANDARD_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
    076            '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',
    077            'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/' };
    078
    079    /**
    080     * This is a copy of the STANDARD_ENCODE_TABLE above, but with + and / changed to - and _ to make the encoded Base64 results more URL-SAFE. This table is
    081     * only used when the Base64's mode is set to URL-SAFE.
    082     */
    083    private static final byte[] URL_SAFE_ENCODE_TABLE = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
    084            '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',
    085            'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '-', '_' };
    086
    087    /**
    088     * Byte used to pad output.
    089     */
    090    private static final byte PAD = '=';
    091
    092    /**
    093     * This array is a lookup table that translates Unicode characters drawn from the "Base64 Alphabet" (as specified in Table 1 of RFC 2045) into their 6-bit
    094     * positive integer equivalents. Characters that are not in the Base64 alphabet but fall within the bounds of the array are translated to -1.
    095     *
    096     * Note: '+' and '-' both decode to 62. '/' and '_' both decode to 63. This means decoder seamlessly handles both URL_SAFE and STANDARD base64. (The
    097     * encoder, on the other hand, needs to know ahead of time what to emit).
    098     *
    099     * Thanks to "commons" project in ws.apache.org for this code. http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
    100     */
    101    private static final byte[] DECODE_TABLE = { -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    102            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1, -1,
    103            0, 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, -1, -1, -1, -1, 63, -1, 26, 27, 28, 29, 30, 31, 32,
    104            33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 };
    105
    106    /** Mask used to extract 6 bits, used when encoding */
    107    private static final int MASK_6BITS = 0x3f;
    108
    109    /** Mask used to extract 8 bits, used in decoding base64 bytes */
    110    private static final int MASK_8BITS = 0xff;
    111
    112    // The static final fields above are used for the original static byte[] methods on Base64.
    113    // The private member fields below are used with the new streaming approach, which requires
    114    // some state be preserved between calls of encode() and decode().
    115
    116    /**
    117     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet.
    118     *
    119     * @param arrayOctet byte array to test
    120     * @return <code>true</code> if any byte is a valid character in the Base64 alphabet; false herwise
    121     */
    122    private static boolean containsBase64Byte(final byte[] arrayOctet) {
    123        for (final byte element : arrayOctet) {
    124            if (isBase64(element)) {
    125                return true;
    126            }
    127        }
    128        return false;
    129    }
    130
    131    /**
    132     * Decodes Base64 data into octets.
    133     *
    134     * @param base64Data Byte array containing Base64 data
    135     * @return Array containing decoded data.
    136     */
    137    public static byte[] decodeBase64(final byte[] base64Data) {
    138        return new Base64().decode(base64Data);
    139    }
    140
    141    /**
    142     * Decodes a Base64 String into octets.
    143     *
    144     * @param base64String String containing Base64 data
    145     * @return Array containing decoded data.
    146     * @since 1.4
    147     */
    148    public static byte[] decodeBase64(final String base64String) {
    149        return new Base64().decode(base64String);
    150    }
    151
    152    // Implementation of integer encoding used for crypto
    153    /**
    154     * Decodes a byte64-encoded integer according to crypto standards such as W3C's XML-Signature
    155     *
    156     * @param pArray a byte array containing base64 character data
    157     * @return A BigInteger
    158     * @since 1.4
    159     */
    160    public static BigInteger decodeInteger(final byte[] pArray) {
    161        return new BigInteger(1, decodeBase64(pArray));
    162    }
    163
    164    /**
    165     * Encodes binary data using the base64 algorithm but does not chunk the output.
    166     *
    167     * @param binaryData binary data to encode
    168     * @return byte[] containing Base64 characters in their UTF-8 representation.
    169     */
    170    public static byte[] encodeBase64(final byte[] binaryData) {
    171        return encodeBase64(binaryData, false);
    172    }
    173
    174    /**
    175     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
    176     *
    177     * @param binaryData Array containing binary data to encode.
    178     * @param isChunked  if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
    179     * @return Base64-encoded data.
    180     * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
    181     */
    182    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked) {
    183        return encodeBase64(binaryData, isChunked, false);
    184    }
    185
    186    /**
    187     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
    188     *
    189     * @param binaryData Array containing binary data to encode.
    190     * @param isChunked  if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
    191     * @param urlSafe    if <code>true</code> this encoder will emit - and _ instead of the usual + and / characters.
    192     * @return Base64-encoded data.
    193     * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than {@link Integer#MAX_VALUE}
    194     * @since 1.4
    195     */
    196    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe) {
    197        return encodeBase64(binaryData, isChunked, urlSafe, Integer.MAX_VALUE);
    198    }
    199
    200    /**
    201     * Encodes binary data using the base64 algorithm, optionally chunking the output into 76 character blocks.
    202     *
    203     * @param binaryData    Array containing binary data to encode.
    204     * @param isChunked     if <code>true</code> this encoder will chunk the base64 output into 76 character blocks
    205     * @param urlSafe       if <code>true</code> this encoder will emit - and _ instead of the usual + and / characters.
    206     * @param maxResultSize The maximum result size to accept.
    207     * @return Base64-encoded data.
    208     * @throws IllegalArgumentException Thrown when the input array needs an output array bigger than maxResultSize
    209     * @since 1.4
    210     */
    211    public static byte[] encodeBase64(final byte[] binaryData, final boolean isChunked, final boolean urlSafe, final int maxResultSize) {
    212        if (binaryData == null || binaryData.length == 0) {
    213            return binaryData;
    214        }
    215
    216        final long len = getEncodeLength(binaryData, isChunked ? CHUNK_SIZE : 0, isChunked ? CHUNK_SEPARATOR : NetConstants.EMPTY_BTYE_ARRAY);
    217        if (len > maxResultSize) {
    218            throw new IllegalArgumentException(
    219                    "Input array too big, the output array would be bigger (" + len + ") than the specified maxium size of " + maxResultSize);
    220        }
    221
    222        final Base64 b64 = isChunked ? new Base64(urlSafe) : new Base64(0, CHUNK_SEPARATOR, urlSafe);
    223        return b64.encode(binaryData);
    224    }
    225
    226    /**
    227     * Encodes binary data using the base64 algorithm and chunks the encoded output into 76 character blocks
    228     *
    229     * @param binaryData binary data to encode
    230     * @return Base64 characters chunked in 76 character blocks
    231     */
    232    public static byte[] encodeBase64Chunked(final byte[] binaryData) {
    233        return encodeBase64(binaryData, true);
    234    }
    235
    236    /**
    237     * Encodes binary data using the base64 algorithm into 76 character blocks separated by CRLF.
    238     * <p>
    239     * For a non-chunking version, see {@link #encodeBase64StringUnChunked(byte[])}.
    240     *
    241     * @param binaryData binary data to encode
    242     * @return String containing Base64 characters.
    243     * @since 1.4
    244     */
    245    public static String encodeBase64String(final byte[] binaryData) {
    246        return newStringUtf8(encodeBase64(binaryData, true));
    247    }
    248
    249    /**
    250     * Encodes binary data using the base64 algorithm.
    251     *
    252     * @param binaryData  binary data to encode
    253     * @param useChunking whether to split the output into chunks
    254     * @return String containing Base64 characters.
    255     * @since 3.2
    256     */
    257    public static String encodeBase64String(final byte[] binaryData, final boolean useChunking) {
    258        return newStringUtf8(encodeBase64(binaryData, useChunking));
    259    }
    260
    261    /**
    262     * Encodes binary data using the base64 algorithm, without using chunking.
    263     * <p>
    264     * For a chunking version, see {@link #encodeBase64String(byte[])}.
    265     *
    266     * @param binaryData binary data to encode
    267     * @return String containing Base64 characters.
    268     * @since 3.2
    269     */
    270    public static String encodeBase64StringUnChunked(final byte[] binaryData) {
    271        return newStringUtf8(encodeBase64(binaryData, false));
    272    }
    273
    274    /**
    275     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The url-safe variation emits - and _ instead of +
    276     * and / characters.
    277     *
    278     * @param binaryData binary data to encode
    279     * @return byte[] containing Base64 characters in their UTF-8 representation.
    280     * @since 1.4
    281     */
    282    public static byte[] encodeBase64URLSafe(final byte[] binaryData) {
    283        return encodeBase64(binaryData, false, true);
    284    }
    285
    286    /**
    287     * Encodes binary data using a URL-safe variation of the base64 algorithm but does not chunk the output. The url-safe variation emits - and _ instead of +
    288     * and / characters.
    289     *
    290     * @param binaryData binary data to encode
    291     * @return String containing Base64 characters
    292     * @since 1.4
    293     */
    294    public static String encodeBase64URLSafeString(final byte[] binaryData) {
    295        return newStringUtf8(encodeBase64(binaryData, false, true));
    296    }
    297
    298    /**
    299     * Encodes to a byte64-encoded integer according to crypto standards such as W3C's XML-Signature
    300     *
    301     * @param bigInt a BigInteger
    302     * @return A byte array containing base64 character data
    303     * @throws NullPointerException if null is passed in
    304     * @since 1.4
    305     */
    306    public static byte[] encodeInteger(final BigInteger bigInt) {
    307        return encodeBase64(toIntegerBytes(bigInt), false);
    308    }
    309
    310    /**
    311     * Pre-calculates the amount of space needed to base64-encode the supplied array.
    312     *
    313     * @param pArray         byte[] array which will later be encoded
    314     * @param chunkSize      line-length of the output (<= 0 means no chunking) between each chunkSeparator (e.g. CRLF).
    315     * @param chunkSeparator the sequence of bytes used to separate chunks of output (e.g. CRLF).
    316     *
    317     * @return amount of space needed to encoded the supplied array. Returns a long since a max-len array will require Integer.MAX_VALUE + 33%.
    318     */
    319    private static long getEncodeLength(final byte[] pArray, int chunkSize, final byte[] chunkSeparator) {
    320        // base64 always encodes to multiples of 4.
    321        chunkSize = (chunkSize / 4) * 4;
    322
    323        long len = (pArray.length * 4) / 3;
    324        final long mod = len % 4;
    325        if (mod != 0) {
    326            len += 4 - mod;
    327        }
    328        if (chunkSize > 0) {
    329            final boolean lenChunksPerfectly = len % chunkSize == 0;
    330            len += (len / chunkSize) * chunkSeparator.length;
    331            if (!lenChunksPerfectly) {
    332                len += chunkSeparator.length;
    333            }
    334        }
    335        return len;
    336    }
    337
    338    /**
    339     * Tests a given byte array to see if it contains only valid characters within the Base64 alphabet. Currently the method treats whitespace as valid.
    340     *
    341     * @param arrayOctet byte array to test
    342     * @return <code>true</code> if all bytes are valid characters in the Base64 alphabet or if the byte array is empty; false, otherwise
    343     */
    344    public static boolean isArrayByteBase64(final byte[] arrayOctet) {
    345        for (final byte element : arrayOctet) {
    346            if (!isBase64(element) && !isWhiteSpace(element)) {
    347                return false;
    348            }
    349        }
    350        return true;
    351    }
    352
    353    /**
    354     * Returns whether or not the <code>octet</code> is in the base 64 alphabet.
    355     *
    356     * @param octet The value to test
    357     * @return <code>true</code> if the value is defined in the the base 64 alphabet, <code>false</code> otherwise.
    358     * @since 1.4
    359     */
    360    public static boolean isBase64(final byte octet) {
    361        return octet == PAD || (octet >= 0 && octet < DECODE_TABLE.length && DECODE_TABLE[octet] != -1);
    362    }
    363
    364    /**
    365     * Checks if a byte value is whitespace or not.
    366     *
    367     * @param byteToCheck the byte to check
    368     * @return true if byte is whitespace, false otherwise
    369     */
    370    private static boolean isWhiteSpace(final byte byteToCheck) {
    371        switch (byteToCheck) {
    372        case ' ':
    373        case '\n':
    374        case '\r':
    375        case '\t':
    376            return true;
    377        default:
    378            return false;
    379        }
    380    }
    381
    382    private static String newStringUtf8(final byte[] encode) {
    383        return new String(encode, StandardCharsets.UTF_8);
    384    }
    385
    386    /**
    387     * Returns a byte-array representation of a <code>BigInteger</code> without sign bit.
    388     *
    389     * @param bigInt <code>BigInteger</code> to be converted
    390     * @return a byte array representation of the BigInteger parameter
    391     */
    392    static byte[] toIntegerBytes(final BigInteger bigInt) {
    393        Objects.requireNonNull(bigInt, "bigInt");
    394        int bitlen = bigInt.bitLength();
    395        // round bitlen
    396        bitlen = ((bitlen + 7) >> 3) << 3;
    397        final byte[] bigBytes = bigInt.toByteArray();
    398
    399        if (((bigInt.bitLength() % 8) != 0) && (((bigInt.bitLength() / 8) + 1) == (bitlen / 8))) {
    400            return bigBytes;
    401        }
    402        // set up params for copying everything but sign bit
    403        int startSrc = 0;
    404        int len = bigBytes.length;
    405
    406        // if bigInt is exactly byte-aligned, just skip signbit in copy
    407        if ((bigInt.bitLength() % 8) == 0) {
    408            startSrc = 1;
    409            len--;
    410        }
    411        final int startDst = bitlen / 8 - len; // to pad w/ nulls as per spec
    412        final byte[] resizedBytes = new byte[bitlen / 8];
    413        System.arraycopy(bigBytes, startSrc, resizedBytes, startDst, len);
    414        return resizedBytes;
    415    }
    416
    417    /**
    418     * Encode table to use: either STANDARD or URL_SAFE. Note: the DECODE_TABLE above remains static because it is able to decode both STANDARD and URL_SAFE
    419     * streams, but the encodeTable must be a member variable so we can switch between the two modes.
    420     */
    421    private final byte[] encodeTable;
    422
    423    /**
    424     * Line length for encoding. Not used when decoding. A value of zero or less implies no chunking of the base64 encoded data.
    425     */
    426    private final int lineLength;
    427
    428    /**
    429     * Line separator for encoding. Not used when decoding. Only used if lineLength > 0.
    430     */
    431    private final byte[] lineSeparator;
    432
    433    /**
    434     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
    435     * <code>decodeSize = 3 + lineSeparator.length;</code>
    436     */
    437    private final int decodeSize;
    438
    439    /**
    440     * Convenience variable to help us determine when our buffer is going to run out of room and needs resizing.
    441     * <code>encodeSize = 4 + lineSeparator.length;</code>
    442     */
    443    private final int encodeSize;
    444
    445    /**
    446     * Buffer for streaming.
    447     */
    448    private byte[] buffer;
    449
    450    /**
    451     * Position where next character should be written in the buffer.
    452     */
    453    private int pos;
    454
    455    /**
    456     * Position where next character should be read from the buffer.
    457     */
    458    private int readPos;
    459
    460    /**
    461     * Variable tracks how many characters have been written to the current line. Only used when encoding. We use it to make sure each encoded line never goes
    462     * beyond lineLength (if lineLength > 0).
    463     */
    464    private int currentLinePos;
    465
    466    /**
    467     * Writes to the buffer only occur after every 3 reads when encoding, an every 4 reads when decoding. This variable helps track that.
    468     */
    469    private int modulus;
    470
    471    /**
    472     * Boolean flag to indicate the EOF has been reached. Once EOF has been reached, this Base64 object becomes useless, and must be thrown away.
    473     */
    474    private boolean eof;
    475
    476    /**
    477     * Place holder for the 3 bytes we're dealing with for our base64 logic. Bitwise operations store and extract the base64 encoding or decoding from this
    478     * variable.
    479     */
    480    private int x;
    481
    482    /**
    483     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
    484     * <p>
    485     * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
    486     * </p>
    487     *
    488     * <p>
    489     * When decoding all variants are supported.
    490     * </p>
    491     */
    492    public Base64() {
    493        this(false);
    494    }
    495
    496    /**
    497     * Creates a Base64 codec used for decoding (all modes) and encoding in the given URL-safe mode.
    498     * <p>
    499     * When encoding the line length is 76, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
    500     * </p>
    501     *
    502     * <p>
    503     * When decoding all variants are supported.
    504     * </p>
    505     *
    506     * @param urlSafe if <code>true</code>, URL-safe encoding is used. In most cases this should be set to <code>false</code>.
    507     * @since 1.4
    508     */
    509    public Base64(final boolean urlSafe) {
    510        this(CHUNK_SIZE, CHUNK_SEPARATOR, urlSafe);
    511    }
    512
    513    /**
    514     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
    515     * <p>
    516     * When encoding the line length is given in the constructor, the line separator is CRLF, and the encoding table is STANDARD_ENCODE_TABLE.
    517     * </p>
    518     * <p>
    519     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
    520     * </p>
    521     * <p>
    522     * When decoding all variants are supported.
    523     * </p>
    524     *
    525     * @param lineLength Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). If {@code lineLength <= 0}, then
    526     *                   the output will not be divided into lines (chunks). Ignored when decoding.
    527     * @since 1.4
    528     */
    529    public Base64(final int lineLength) {
    530        this(lineLength, CHUNK_SEPARATOR);
    531    }
    532
    533    /**
    534     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
    535     * <p>
    536     * When encoding the line length and line separator are given in the constructor, and the encoding table is STANDARD_ENCODE_TABLE.
    537     * </p>
    538     * <p>
    539     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
    540     * </p>
    541     * <p>
    542     * When decoding all variants are supported.
    543     * </p>
    544     *
    545     * @param lineLength    Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). If {@code lineLength <= 0},
    546     *                      then the output will not be divided into lines (chunks). Ignored when decoding.
    547     * @param lineSeparator Each line of encoded data will end with this sequence of bytes.
    548     * @throws IllegalArgumentException Thrown when the provided lineSeparator included some base64 characters.
    549     * @since 1.4
    550     */
    551    public Base64(final int lineLength, final byte[] lineSeparator) {
    552        this(lineLength, lineSeparator, false);
    553    }
    554
    555    /**
    556     * Creates a Base64 codec used for decoding (all modes) and encoding in URL-unsafe mode.
    557     * <p>
    558     * When encoding the line length and line separator are given in the constructor, and the encoding table is STANDARD_ENCODE_TABLE.
    559     * </p>
    560     * <p>
    561     * Line lengths that aren't multiples of 4 will still essentially end up being multiples of 4 in the encoded data.
    562     * </p>
    563     * <p>
    564     * When decoding all variants are supported.
    565     * </p>
    566     *
    567     * @param lineLength    Each line of encoded data will be at most of the given length (rounded down to nearest multiple of 4). If {@code lineLength <= 0},
    568     *                      then the output will not be divided into lines (chunks). Ignored when decoding.
    569     * @param lineSeparator Each line of encoded data will end with this sequence of bytes.
    570     * @param urlSafe       Instead of emitting '+' and '/' we emit '-' and '_' respectively. urlSafe is only applied to encode operations. Decoding seamlessly
    571     *                      handles both modes.
    572     * @throws IllegalArgumentException The provided lineSeparator included some base64 characters. That's not going to work!
    573     * @since 1.4
    574     */
    575    public Base64(int lineLength, byte[] lineSeparator, final boolean urlSafe) {
    576        if (lineSeparator == null) {
    577            lineLength = 0; // disable chunk-separating
    578            lineSeparator = NetConstants.EMPTY_BTYE_ARRAY; // this just gets ignored
    579        }
    580        this.lineLength = lineLength > 0 ? (lineLength / 4) * 4 : 0;
    581        this.lineSeparator = new byte[lineSeparator.length];
    582        System.arraycopy(lineSeparator, 0, this.lineSeparator, 0, lineSeparator.length);
    583        if (lineLength > 0) {
    584            this.encodeSize = 4 + lineSeparator.length;
    585        } else {
    586            this.encodeSize = 4;
    587        }
    588        this.decodeSize = this.encodeSize - 1;
    589        if (containsBase64Byte(lineSeparator)) {
    590            final String sep = newStringUtf8(lineSeparator);
    591            throw new IllegalArgumentException("lineSeperator must not contain base64 characters: [" + sep + "]");
    592        }
    593        this.encodeTable = urlSafe ? URL_SAFE_ENCODE_TABLE : STANDARD_ENCODE_TABLE;
    594    }
    595
    596    /**
    597     * Returns the amount of buffered data available for reading.
    598     *
    599     * @return The amount of buffered data available for reading.
    600     */
    601    int avail() {
    602        return buffer != null ? pos - readPos : 0;
    603    }
    604
    605    /**
    606     * Decodes a byte[] containing containing characters in the Base64 alphabet.
    607     *
    608     * @param pArray A byte array containing Base64 character data
    609     * @return a byte array containing binary data
    610     */
    611    public byte[] decode(final byte[] pArray) {
    612        reset();
    613        if (pArray == null || pArray.length == 0) {
    614            return pArray;
    615        }
    616        final long len = (pArray.length * 3) / 4;
    617        final byte[] buf = new byte[(int) len];
    618        setInitialBuffer(buf, 0, buf.length);
    619        decode(pArray, 0, pArray.length);
    620        decode(pArray, 0, -1); // Notify decoder of EOF.
    621
    622        // Would be nice to just return buf (like we sometimes do in the encode
    623        // logic), but we have no idea what the line-length was (could even be
    624        // variable). So we cannot determine ahead of time exactly how big an
    625        // array is necessary. Hence the need to construct a 2nd byte array to
    626        // hold the final result:
    627
    628        final byte[] result = new byte[pos];
    629        readResults(result, 0, result.length);
    630        return result;
    631    }
    632
    633    /**
    634     * <p>
    635     * Decodes all of the provided data, starting at inPos, for inAvail bytes. Should be called at least twice: once with the data to decode, and once with
    636     * inAvail set to "-1" to alert decoder that EOF has been reached. The "-1" call is not necessary when decoding, but it doesn't hurt, either.
    637     * </p>
    638     * <p>
    639     * Ignores all non-base64 characters. This is how chunked (e.g. 76 character) data is handled, since CR and LF are silently ignored, but has implications
    640     * for other bytes, too. This method subscribes to the garbage-in, garbage-out philosophy: it will not check the provided data for validity.
    641     * </p>
    642     * <p>
    643     * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
    644     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
    645     * </p>
    646     *
    647     * @param in      byte[] array of ascii data to base64 decode.
    648     * @param inPos   Position to start reading data from.
    649     * @param inAvail Amount of bytes available from input for encoding.
    650     */
    651    void decode(final byte[] in, int inPos, final int inAvail) {
    652        if (eof) {
    653            return;
    654        }
    655        if (inAvail < 0) {
    656            eof = true;
    657        }
    658        for (int i = 0; i < inAvail; i++) {
    659            if (buffer == null || buffer.length - pos < decodeSize) {
    660                resizeBuffer();
    661            }
    662            final byte b = in[inPos++];
    663            if (b == PAD) {
    664                // We're done.
    665                eof = true;
    666                break;
    667            }
    668            if (b >= 0 && b < DECODE_TABLE.length) {
    669                final int result = DECODE_TABLE[b];
    670                if (result >= 0) {
    671                    modulus = (++modulus) % 4;
    672                    x = (x << 6) + result;
    673                    if (modulus == 0) {
    674                        buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
    675                        buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
    676                        buffer[pos++] = (byte) (x & MASK_8BITS);
    677                    }
    678                }
    679            }
    680        }
    681
    682        // Two forms of EOF as far as base64 decoder is concerned: actual
    683        // EOF (-1) and first time '=' character is encountered in stream.
    684        // This approach makes the '=' padding characters completely optional.
    685        if (eof && modulus != 0) {
    686            x = x << 6;
    687            switch (modulus) {
    688            case 2:
    689                x = x << 6;
    690                buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
    691                break;
    692            case 3:
    693                buffer[pos++] = (byte) ((x >> 16) & MASK_8BITS);
    694                buffer[pos++] = (byte) ((x >> 8) & MASK_8BITS);
    695                break;
    696            default:
    697                break; // other values ignored
    698            }
    699        }
    700    }
    701
    702    /**
    703     * Decodes a String containing containing characters in the Base64 alphabet.
    704     *
    705     * @param pArray A String containing Base64 character data
    706     * @return a byte array containing binary data
    707     * @since 1.4
    708     */
    709    public byte[] decode(final String pArray) {
    710        return decode(getBytesUtf8(pArray));
    711    }
    712
    713    /**
    714     * Encodes a byte[] containing binary data, into a byte[] containing characters in the Base64 alphabet.
    715     *
    716     * @param pArray a byte array containing binary data
    717     * @return A byte array containing only Base64 character data
    718     */
    719    public byte[] encode(final byte[] pArray) {
    720        reset();
    721        if (pArray == null || pArray.length == 0) {
    722            return pArray;
    723        }
    724        final long len = getEncodeLength(pArray, lineLength, lineSeparator);
    725        byte[] buf = new byte[(int) len];
    726        setInitialBuffer(buf, 0, buf.length);
    727        encode(pArray, 0, pArray.length);
    728        encode(pArray, 0, -1); // Notify encoder of EOF.
    729        // Encoder might have resized, even though it was unnecessary.
    730        if (buffer != buf) {
    731            readResults(buf, 0, buf.length);
    732        }
    733        // In URL-SAFE mode we skip the padding characters, so sometimes our
    734        // final length is a bit smaller.
    735        if (isUrlSafe() && pos < buf.length) {
    736            final byte[] smallerBuf = new byte[pos];
    737            System.arraycopy(buf, 0, smallerBuf, 0, pos);
    738            buf = smallerBuf;
    739        }
    740        return buf;
    741    }
    742
    743    /**
    744     * <p>
    745     * Encodes all of the provided data, starting at inPos, for inAvail bytes. Must be called at least twice: once with the data to encode, and once with
    746     * inAvail set to "-1" to alert encoder that EOF has been reached, so flush last remaining bytes (if not multiple of 3).
    747     * </p>
    748     * <p>
    749     * Thanks to "commons" project in ws.apache.org for the bitwise operations, and general approach.
    750     * http://svn.apache.org/repos/asf/webservices/commons/trunk/modules/util/
    751     * </p>
    752     *
    753     * @param in      byte[] array of binary data to base64 encode.
    754     * @param inPos   Position to start reading data from.
    755     * @param inAvail Amount of bytes available from input for encoding.
    756     */
    757    void encode(final byte[] in, int inPos, final int inAvail) {
    758        if (eof) {
    759            return;
    760        }
    761        // inAvail < 0 is how we're informed of EOF in the underlying data we're
    762        // encoding.
    763        if (inAvail < 0) {
    764            eof = true;
    765            if (buffer == null || buffer.length - pos < encodeSize) {
    766                resizeBuffer();
    767            }
    768            switch (modulus) {
    769            case 1:
    770                buffer[pos++] = encodeTable[(x >> 2) & MASK_6BITS];
    771                buffer[pos++] = encodeTable[(x << 4) & MASK_6BITS];
    772                // URL-SAFE skips the padding to further reduce size.
    773                if (encodeTable == STANDARD_ENCODE_TABLE) {
    774                    buffer[pos++] = PAD;
    775                    buffer[pos++] = PAD;
    776                }
    777                break;
    778
    779            case 2:
    780                buffer[pos++] = encodeTable[(x >> 10) & MASK_6BITS];
    781                buffer[pos++] = encodeTable[(x >> 4) & MASK_6BITS];
    782                buffer[pos++] = encodeTable[(x << 2) & MASK_6BITS];
    783                // URL-SAFE skips the padding to further reduce size.
    784                if (encodeTable == STANDARD_ENCODE_TABLE) {
    785                    buffer[pos++] = PAD;
    786                }
    787                break;
    788            default:
    789                break; // other values ignored
    790            }
    791            if (lineLength > 0 && pos > 0) {
    792                System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
    793                pos += lineSeparator.length;
    794            }
    795        } else {
    796            for (int i = 0; i < inAvail; i++) {
    797                if (buffer == null || buffer.length - pos < encodeSize) {
    798                    resizeBuffer();
    799                }
    800                modulus = (++modulus) % 3;
    801                int b = in[inPos++];
    802                if (b < 0) {
    803                    b += 256;
    804                }
    805                x = (x << 8) + b;
    806                if (0 == modulus) {
    807                    buffer[pos++] = encodeTable[(x >> 18) & MASK_6BITS];
    808                    buffer[pos++] = encodeTable[(x >> 12) & MASK_6BITS];
    809                    buffer[pos++] = encodeTable[(x >> 6) & MASK_6BITS];
    810                    buffer[pos++] = encodeTable[x & MASK_6BITS];
    811                    currentLinePos += 4;
    812                    if (lineLength > 0 && lineLength <= currentLinePos) {
    813                        System.arraycopy(lineSeparator, 0, buffer, pos, lineSeparator.length);
    814                        pos += lineSeparator.length;
    815                        currentLinePos = 0;
    816                    }
    817                }
    818            }
    819        }
    820    }
    821
    822    /**
    823     * Encodes a byte[] containing binary data, into a String containing characters in the Base64 alphabet.
    824     *
    825     * @param pArray a byte array containing binary data
    826     * @return A String containing only Base64 character data
    827     * @since 1.4
    828     */
    829    public String encodeToString(final byte[] pArray) {
    830        return newStringUtf8(encode(pArray));
    831    }
    832
    833    private byte[] getBytesUtf8(final String pArray) {
    834        return pArray.getBytes(StandardCharsets.UTF_8);
    835    }
    836
    837    int getLineLength() {
    838        return lineLength;
    839    }
    840
    841    byte[] getLineSeparator() {
    842        return lineSeparator.clone();
    843    }
    844
    845    /**
    846     * Returns true if this Base64 object has buffered data for reading.
    847     *
    848     * @return true if there is Base64 object still available for reading.
    849     */
    850    boolean hasData() {
    851        return this.buffer != null;
    852    }
    853
    854    /**
    855     * Returns our current encode mode. True if we're URL-SAFE, false otherwise.
    856     *
    857     * @return true if we're in URL-SAFE mode, false otherwise.
    858     * @since 1.4
    859     */
    860    public boolean isUrlSafe() {
    861        return this.encodeTable == URL_SAFE_ENCODE_TABLE;
    862    }
    863
    864    /**
    865     * Extracts buffered data into the provided byte[] array, starting at position bPos, up to a maximum of bAvail bytes. Returns how many bytes were actually
    866     * extracted.
    867     *
    868     * @param b      byte[] array to extract the buffered data into.
    869     * @param bPos   position in byte[] array to start extraction at.
    870     * @param bAvail amount of bytes we're allowed to extract. We may extract fewer (if fewer are available).
    871     * @return The number of bytes successfully extracted into the provided byte[] array.
    872     */
    873    int readResults(final byte[] b, final int bPos, final int bAvail) {
    874        if (buffer != null) {
    875            final int len = Math.min(avail(), bAvail);
    876            if (buffer != b) {
    877                System.arraycopy(buffer, readPos, b, bPos, len);
    878                readPos += len;
    879                if (readPos >= pos) {
    880                    buffer = null;
    881                }
    882            } else {
    883                // Re-using the original consumer's output array is only
    884                // allowed for one round.
    885                buffer = null;
    886            }
    887            return len;
    888        }
    889        return eof ? -1 : 0;
    890    }
    891
    892    /**
    893     * Resets this Base64 object to its initial newly constructed state.
    894     */
    895    private void reset() {
    896        buffer = null;
    897        pos = 0;
    898        readPos = 0;
    899        currentLinePos = 0;
    900        modulus = 0;
    901        eof = false;
    902    }
    903
    904    // Getters for use in testing
    905
    906    /** Doubles our buffer. */
    907    private void resizeBuffer() {
    908        if (buffer == null) {
    909            buffer = new byte[DEFAULT_BUFFER_SIZE];
    910            pos = 0;
    911            readPos = 0;
    912        } else {
    913            final byte[] b = new byte[buffer.length * DEFAULT_BUFFER_RESIZE_FACTOR];
    914            System.arraycopy(buffer, 0, b, 0, buffer.length);
    915            buffer = b;
    916        }
    917    }
    918
    919    /**
    920     * Sets the streaming buffer. This is a small optimization where we try to buffer directly to the consumer's output array for one round (if the consumer
    921     * calls this method first) instead of starting our own buffer.
    922     *
    923     * @param out      byte[] array to buffer directly to.
    924     * @param outPos   Position to start buffering into.
    925     * @param outAvail Amount of bytes available for direct buffering.
    926     */
    927    void setInitialBuffer(final byte[] out, final int outPos, final int outAvail) {
    928        // We can re-use consumer's original output array under
    929        // special circumstances, saving on some System.arraycopy().
    930        if (out != null && out.length == outAvail) {
    931            buffer = out;
    932            pos = outPos;
    933            readPos = outPos;
    934        }
    935    }
    936}