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