001/*
002 * Copyright 2012-2018 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright (C) 2012-2018 Ping Identity Corporation
007 *
008 * This program is free software; you can redistribute it and/or modify
009 * it under the terms of the GNU General Public License (GPLv2 only)
010 * or the terms of the GNU Lesser General Public License (LGPLv2.1 only)
011 * as published by the Free Software Foundation.
012 *
013 * This program is distributed in the hope that it will be useful,
014 * but WITHOUT ANY WARRANTY; without even the implied warranty of
015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
016 * GNU General Public License for more details.
017 *
018 * You should have received a copy of the GNU General Public License
019 * along with this program; if not, see <http://www.gnu.org/licenses>.
020 */
021package com.unboundid.util;
022
023
024
025import java.io.IOException;
026import java.text.ParseException;
027
028import static com.unboundid.util.UtilityMessages.*;
029
030
031
032/**
033 * This class provides methods for encoding and decoding data in base32 as
034 * defined in <A HREF="http://www.ietf.org/rfc/rfc4648.txt">RFC 4648</A>.  It
035 * provides a somewhat compact way of representing binary data using only
036 * printable characters (a subset of ASCII letters and numeric digits selected
037 * to avoid ambiguity, like confusion between the number 1 and the uppercase
038 * letter I, and between the number 0 and the uppercase letter O).  It uses a
039 * five-bit encoding mechanism in which every five bytes of raw data is
040 * converted into eight bytes of base32-encoded data.
041 * <BR><BR>
042 * <H2>Example</H2>
043 * The following examples demonstrate the process for base32-encoding raw data,
044 * and for decoding a string containing base32-encoded data back to the raw
045 * data used to create it:
046 * <PRE>
047 * // Base32-encode some raw data:
048 * String base32String = Base32.encode(rawDataBytes);
049 *
050 * // Decode a base32 string back to raw data:
051 * byte[] decodedRawDataBytes;
052 * try
053 * {
054 *   decodedRawDataBytes = Base32.decode(base32String);
055 * }
056 * catch (ParseException pe)
057 * {
058 *   // The string did not represent a valid base32 encoding.
059 *   decodedRawDataBytes = null;
060 * }
061 * </PRE>
062 */
063@ThreadSafety(level=ThreadSafetyLevel.COMPLETELY_THREADSAFE)
064public final class Base32
065{
066  /**
067   * The set of characters in the base32 alphabet.
068   */
069  private static final char[] BASE32_ALPHABET =
070       ("ABCDEFGHIJKLMNOPQRSTUVWXYZ234567").toCharArray();
071
072
073
074  /**
075   * Prevent this class from being instantiated.
076   */
077  private Base32()
078  {
079    // No implementation is required.
080  }
081
082
083
084  /**
085   * Encodes the UTF-8 representation of the provided string in base32 format.
086   *
087   * @param  data  The raw data to be encoded.  It must not be {@code null}.
088   *
089   * @return  The base32-encoded representation of the provided data.
090   */
091  public static String encode(final String data)
092  {
093    Validator.ensureNotNull(data);
094
095    return encode(StaticUtils.getBytes(data));
096  }
097
098
099
100  /**
101   * Encodes the provided data in base32 format.
102   *
103   * @param  data  The raw data to be encoded.  It must not be {@code null}.
104   *
105   * @return  The base32-encoded representation of the provided data.
106   */
107  public static String encode(final byte[] data)
108  {
109    Validator.ensureNotNull(data);
110
111    final StringBuilder buffer = new StringBuilder(4*data.length/3+1);
112    encodeInternal(data, 0, data.length, buffer);
113    return buffer.toString();
114  }
115
116
117
118  /**
119   * Appends a base32-encoded version of the contents of the provided buffer
120   * (using a UTF-8 representation) to the given buffer.
121   *
122   * @param  data    The raw data to be encoded.  It must not be {@code null}.
123   * @param  buffer  The buffer to which the base32-encoded data is to be
124   *                 written.
125   */
126  public static void encode(final String data, final StringBuilder buffer)
127  {
128    Validator.ensureNotNull(data);
129
130    encode(StaticUtils.getBytes(data), buffer);
131  }
132
133
134
135  /**
136   * Appends a base32-encoded version of the contents of the provided buffer
137   * (using a UTF-8 representation) to the given buffer.
138   *
139   * @param  data    The raw data to be encoded.  It must not be {@code null}.
140   * @param  buffer  The buffer to which the base32-encoded data is to be
141   *                 written.
142   */
143  public static void encode(final String data, final ByteStringBuffer buffer)
144  {
145    Validator.ensureNotNull(data);
146
147    encode(StaticUtils.getBytes(data), buffer);
148  }
149
150
151
152  /**
153   * Appends a base32-encoded representation of the provided data to the given
154   * buffer.
155   *
156   * @param  data    The raw data to be encoded.  It must not be {@code null}.
157   * @param  buffer  The buffer to which the base32-encoded data is to be
158   *                 written.
159   */
160  public static void encode(final byte[] data, final StringBuilder buffer)
161  {
162    encodeInternal(data, 0, data.length, buffer);
163  }
164
165
166
167  /**
168   * Appends a base32-encoded representation of the provided data to the given
169   * buffer.
170   *
171   * @param  data    The array containing the raw data to be encoded.  It must
172   *                 not be {@code null}.
173   * @param  off     The offset in the array at which the data to encode begins.
174   * @param  length  The number of bytes to be encoded.
175   * @param  buffer  The buffer to which the base32-encoded data is to be
176   *                 written.
177   */
178  public static void encode(final byte[] data, final int off, final int length,
179                            final StringBuilder buffer)
180  {
181    encodeInternal(data, off, length, buffer);
182  }
183
184
185
186  /**
187   * Appends a base32-encoded representation of the provided data to the given
188   * buffer.
189   *
190   * @param  data    The raw data to be encoded.  It must not be {@code null}.
191   * @param  buffer  The buffer to which the base32-encoded data is to be
192   *                 written.
193   */
194  public static void encode(final byte[] data, final ByteStringBuffer buffer)
195  {
196    encodeInternal(data, 0, data.length, buffer);
197  }
198
199
200
201  /**
202   * Appends a base32-encoded representation of the provided data to the given
203   * buffer.
204   *
205   * @param  data    The raw data to be encoded.  It must not be {@code null}.
206   * @param  off     The offset in the array at which the data to encode begins.
207   * @param  length  The number of bytes to be encoded.
208   * @param  buffer  The buffer to which the base32-encoded data is to be
209   *                 written.
210   */
211  public static void encode(final byte[] data, final int off, final int length,
212                            final ByteStringBuffer buffer)
213  {
214    encodeInternal(data, off, length, buffer);
215  }
216
217
218
219  /**
220   * Appends a base32-encoded representation of the provided data to the given
221   * buffer.
222   *
223   * @param  data    The raw data to be encoded.  It must not be {@code null}.
224   * @param  off     The offset in the array at which the data to encode begins.
225   * @param  length  The number of bytes to be encoded.
226   * @param  buffer  The buffer to which the base32-encoded data is to be
227   *                 written.
228   */
229  private static void encodeInternal(final byte[] data, final int off,
230                                     final int length, final Appendable buffer)
231  {
232    Validator.ensureNotNull(data);
233    Validator.ensureTrue(data.length >= off);
234    Validator.ensureTrue(data.length >= (off+length));
235
236    if (length == 0)
237    {
238      return;
239    }
240
241    try
242    {
243      int pos = off;
244      for (int i=0; i < (length / 5); i++)
245      {
246        final long longValue =
247             (((data[pos++] & 0xFFL) << 32) |
248              ((data[pos++] & 0xFFL) << 24) |
249              ((data[pos++] & 0xFFL) << 16) |
250              ((data[pos++] & 0xFFL) << 8) |
251               (data[pos++] & 0xFFL));
252
253        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
254        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
255        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 25) & 0x1FL)]);
256        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 20) & 0x1FL)]);
257        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 15) & 0x1FL)]);
258        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 10) & 0x1FL)]);
259        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 5) & 0x1FL)]);
260        buffer.append(BASE32_ALPHABET[(int) (longValue & 0x1FL)]);
261      }
262
263      switch ((off+length) - pos)
264      {
265        case 1:
266          long longValue = ((data[pos] & 0xFFL) << 32);
267          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
268          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
269          buffer.append("======");
270          return;
271
272        case 2:
273          longValue = (((data[pos++] & 0xFFL) << 32) |
274                       ((data[pos] & 0xFFL) << 24));
275          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
276          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
277          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 25) & 0x1FL)]);
278          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 20) & 0x1FL)]);
279          buffer.append("====");
280          return;
281
282        case 3:
283          longValue = (((data[pos++] & 0xFFL) << 32) |
284                       ((data[pos++] & 0xFFL) << 24) |
285                       ((data[pos] & 0xFFL) << 16));
286          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
287          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
288          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 25) & 0x1FL)]);
289          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 20) & 0x1FL)]);
290          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 15) & 0x1FL)]);
291          buffer.append("===");
292          return;
293
294        case 4:
295          longValue = (((data[pos++] & 0xFFL) << 32) |
296                       ((data[pos++] & 0xFFL) << 24) |
297                       ((data[pos++] & 0xFFL) << 16) |
298                       ((data[pos] & 0xFFL) << 8));
299          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
300          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
301          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 25) & 0x1FL)]);
302          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 20) & 0x1FL)]);
303          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 15) & 0x1FL)]);
304          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 10) & 0x1FL)]);
305          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 5) & 0x1FL)]);
306          buffer.append("=");
307          return;
308      }
309    }
310    catch (final IOException ioe)
311    {
312      Debug.debugException(ioe);
313
314      // This should never happen.
315      throw new RuntimeException(ioe.getMessage(), ioe);
316    }
317  }
318
319
320
321  /**
322   * Decodes the contents of the provided base32-encoded string.
323   *
324   * @param  data  The base32-encoded string to decode.  It must not be
325   *               {@code null}.
326   *
327   * @return  A byte array containing the decoded data.
328   *
329   * @throws  ParseException  If the contents of the provided string cannot be
330   *                          parsed as base32-encoded data.
331   */
332  public static byte[] decode(final String data)
333         throws ParseException
334  {
335    Validator.ensureNotNull(data);
336
337    final int length = data.length();
338    if (length == 0)
339    {
340      return StaticUtils.NO_BYTES;
341    }
342
343    if ((length % 8) != 0)
344    {
345      throw new ParseException(ERR_BASE32_DECODE_INVALID_LENGTH.get(), length);
346    }
347
348    final ByteStringBuffer buffer = new ByteStringBuffer(5 * (length / 8));
349
350    int stringPos = 0;
351    while (stringPos < length)
352    {
353      long longValue = 0x00;
354      for (int i=0; i < 8; i++)
355      {
356        longValue <<= 5;
357        switch (data.charAt(stringPos++))
358        {
359          case 'A':
360          case 'a':
361            longValue |= 0x00L;
362            break;
363          case 'B':
364          case 'b':
365            longValue |= 0x01L;
366            break;
367          case 'C':
368          case 'c':
369            longValue |= 0x02L;
370            break;
371          case 'D':
372          case 'd':
373            longValue |= 0x03L;
374            break;
375          case 'E':
376          case 'e':
377            longValue |= 0x04L;
378            break;
379          case 'F':
380          case 'f':
381            longValue |= 0x05L;
382            break;
383          case 'G':
384          case 'g':
385            longValue |= 0x06L;
386            break;
387          case 'H':
388          case 'h':
389            longValue |= 0x07L;
390            break;
391          case 'I':
392          case 'i':
393            longValue |= 0x08L;
394            break;
395          case 'J':
396          case 'j':
397            longValue |= 0x09L;
398            break;
399          case 'K':
400          case 'k':
401            longValue |= 0x0AL;
402            break;
403          case 'L':
404          case 'l':
405            longValue |= 0x0BL;
406            break;
407          case 'M':
408          case 'm':
409            longValue |= 0x0CL;
410            break;
411          case 'N':
412          case 'n':
413            longValue |= 0x0DL;
414            break;
415          case 'O':
416          case 'o':
417            longValue |= 0x0EL;
418            break;
419          case 'P':
420          case 'p':
421            longValue |= 0x0FL;
422            break;
423          case 'Q':
424          case 'q':
425            longValue |= 0x10L;
426            break;
427          case 'R':
428          case 'r':
429            longValue |= 0x11L;
430            break;
431          case 'S':
432          case 's':
433            longValue |= 0x12L;
434            break;
435          case 'T':
436          case 't':
437            longValue |= 0x13L;
438            break;
439          case 'U':
440          case 'u':
441            longValue |= 0x14L;
442            break;
443          case 'V':
444          case 'v':
445            longValue |= 0x15L;
446            break;
447          case 'W':
448          case 'w':
449            longValue |= 0x16L;
450            break;
451          case 'X':
452          case 'x':
453            longValue |= 0x17L;
454            break;
455          case 'Y':
456          case 'y':
457            longValue |= 0x18L;
458            break;
459          case 'Z':
460          case 'z':
461            longValue |= 0x19L;
462            break;
463          case '2':
464            longValue |= 0x1AL;
465            break;
466          case '3':
467            longValue |= 0x1BL;
468            break;
469          case '4':
470            longValue |= 0x1CL;
471            break;
472          case '5':
473            longValue |= 0x1DL;
474            break;
475          case '6':
476            longValue |= 0x1EL;
477            break;
478          case '7':
479            longValue |= 0x1FL;
480            break;
481
482          case '=':
483            switch (length - stringPos)
484            {
485              case 0:
486                // The string ended with a single equal sign, so there are
487                // four bytes left.
488                buffer.append((byte) ((longValue >> 32) & 0xFFL));
489                buffer.append((byte) ((longValue >> 24) & 0xFFL));
490                buffer.append((byte) ((longValue >> 16) & 0xFFL));
491                buffer.append((byte) ((longValue >> 8) & 0xFFL));
492                return buffer.toByteArray();
493
494              case 2:
495                // The string ended with three equal signs, so there are three
496                // bytes left.
497                longValue <<= 10;
498                buffer.append((byte) ((longValue >> 32) & 0xFFL));
499                buffer.append((byte) ((longValue >> 24) & 0xFFL));
500                buffer.append((byte) ((longValue >> 16) & 0xFFL));
501                return buffer.toByteArray();
502
503              case 3:
504                // The string ended with four equal signs, so there are two
505                // bytes left.
506                longValue <<= 15;
507                buffer.append((byte) ((longValue >> 32) & 0xFFL));
508                buffer.append((byte) ((longValue >> 24) & 0xFFL));
509                return buffer.toByteArray();
510
511              case 5:
512                // The string ended with six equal signs, so there is one byte
513                // left.
514                longValue <<= 25;
515                buffer.append((byte) ((longValue >> 32) & 0xFFL));
516                return buffer.toByteArray();
517
518              default:
519                throw new ParseException(
520                     ERR_BASE32_DECODE_UNEXPECTED_EQUAL.get((stringPos-1)),
521                     (stringPos-1));
522            }
523
524          default:
525            throw new ParseException(
526                 ERR_BASE32_DECODE_UNEXPECTED_CHAR.get(
527                      data.charAt(stringPos-1)),
528                 (stringPos-1));
529        }
530      }
531
532      buffer.append((byte) ((longValue >> 32) & 0xFFL));
533      buffer.append((byte) ((longValue >> 24) & 0xFFL));
534      buffer.append((byte) ((longValue >> 16) & 0xFFL));
535      buffer.append((byte) ((longValue >> 8) & 0xFFL));
536      buffer.append((byte) (longValue & 0xFFL));
537    }
538
539    return buffer.toByteArray();
540  }
541
542
543
544  /**
545   * Decodes the contents of the provided base32-encoded string to a string
546   * containing the raw data using the UTF-8 encoding.
547   *
548   * @param  data  The base32-encoded string to decode.  It must not be
549   *               {@code null}.
550   *
551   * @return  A string containing the decoded data.
552   *
553   * @throws  ParseException  If the contents of the provided string cannot be
554   *                          parsed as base32-encoded data using the UTF-8
555   *                          encoding.
556   */
557  public static String decodeToString(final String data)
558         throws ParseException
559  {
560    Validator.ensureNotNull(data);
561
562    final byte[] decodedBytes = decode(data);
563    return StaticUtils.toUTF8String(decodedBytes);
564  }
565}