001/*
002 * Copyright 2012-2024 Ping Identity Corporation
003 * All Rights Reserved.
004 */
005/*
006 * Copyright 2012-2024 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-2024 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  @NotNull 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  @NotNull()
107  public static String encode(@NotNull final String data)
108  {
109    Validator.ensureNotNull(data);
110
111    return encode(StaticUtils.getBytes(data));
112  }
113
114
115
116  /**
117   * Encodes the provided data in base32 format.
118   *
119   * @param  data  The raw data to be encoded.  It must not be {@code null}.
120   *
121   * @return  The base32-encoded representation of the provided data.
122   */
123  @NotNull()
124  public static String encode(@NotNull final byte[] data)
125  {
126    Validator.ensureNotNull(data);
127
128    final StringBuilder buffer = new StringBuilder(4*data.length/3+1);
129    encodeInternal(data, 0, data.length, buffer);
130    return buffer.toString();
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(@NotNull final String data,
144                            @NotNull final StringBuilder buffer)
145  {
146    Validator.ensureNotNull(data);
147
148    encode(StaticUtils.getBytes(data), buffer);
149  }
150
151
152
153  /**
154   * Appends a base32-encoded version of the contents of the provided buffer
155   * (using a UTF-8 representation) to the given buffer.
156   *
157   * @param  data    The raw data to be encoded.  It must not be {@code null}.
158   * @param  buffer  The buffer to which the base32-encoded data is to be
159   *                 written.
160   */
161  public static void encode(@NotNull final String data,
162                            @NotNull final ByteStringBuffer buffer)
163  {
164    Validator.ensureNotNull(data);
165
166    encode(StaticUtils.getBytes(data), buffer);
167  }
168
169
170
171  /**
172   * Appends a base32-encoded representation of the provided data to the given
173   * buffer.
174   *
175   * @param  data    The raw data to be encoded.  It must not be {@code null}.
176   * @param  buffer  The buffer to which the base32-encoded data is to be
177   *                 written.
178   */
179  public static void encode(@NotNull final byte[] data,
180                            @NotNull final StringBuilder buffer)
181  {
182    encodeInternal(data, 0, data.length, buffer);
183  }
184
185
186
187  /**
188   * Appends a base32-encoded representation of the provided data to the given
189   * buffer.
190   *
191   * @param  data    The array containing the raw data to be encoded.  It must
192   *                 not be {@code null}.
193   * @param  off     The offset in the array at which the data to encode begins.
194   * @param  length  The number of bytes to be encoded.
195   * @param  buffer  The buffer to which the base32-encoded data is to be
196   *                 written.
197   */
198  public static void encode(@NotNull final byte[] data, final int off,
199                            final int length,
200                            @NotNull final StringBuilder buffer)
201  {
202    encodeInternal(data, off, length, buffer);
203  }
204
205
206
207  /**
208   * Appends a base32-encoded representation of the provided data to the given
209   * buffer.
210   *
211   * @param  data    The raw data to be encoded.  It must not be {@code null}.
212   * @param  buffer  The buffer to which the base32-encoded data is to be
213   *                 written.
214   */
215  public static void encode(@NotNull final byte[] data,
216                            @NotNull final ByteStringBuffer buffer)
217  {
218    encodeInternal(data, 0, data.length, buffer);
219  }
220
221
222
223  /**
224   * Appends a base32-encoded representation of the provided data to the given
225   * buffer.
226   *
227   * @param  data    The raw data to be encoded.  It must not be {@code null}.
228   * @param  off     The offset in the array at which the data to encode begins.
229   * @param  length  The number of bytes to be encoded.
230   * @param  buffer  The buffer to which the base32-encoded data is to be
231   *                 written.
232   */
233  public static void encode(@NotNull final byte[] data, final int off,
234                            final int length,
235                            @NotNull final ByteStringBuffer buffer)
236  {
237    encodeInternal(data, off, length, buffer);
238  }
239
240
241
242  /**
243   * Appends a base32-encoded representation of the provided data to the given
244   * buffer.
245   *
246   * @param  data    The raw data to be encoded.  It must not be {@code null}.
247   * @param  off     The offset in the array at which the data to encode begins.
248   * @param  length  The number of bytes to be encoded.
249   * @param  buffer  The buffer to which the base32-encoded data is to be
250   *                 written.
251   */
252  private static void encodeInternal(@NotNull final byte[] data, final int off,
253                                     final int length,
254                                     @NotNull final Appendable buffer)
255  {
256    Validator.ensureNotNull(data);
257    Validator.ensureTrue(data.length >= off);
258    Validator.ensureTrue(data.length >= (off+length));
259
260    if (length == 0)
261    {
262      return;
263    }
264
265    try
266    {
267      int pos = off;
268      for (int i=0; i < (length / 5); i++)
269      {
270        final long longValue =
271             (((data[pos++] & 0xFFL) << 32) |
272              ((data[pos++] & 0xFFL) << 24) |
273              ((data[pos++] & 0xFFL) << 16) |
274              ((data[pos++] & 0xFFL) << 8) |
275               (data[pos++] & 0xFFL));
276
277        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
278        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
279        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 25) & 0x1FL)]);
280        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 20) & 0x1FL)]);
281        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 15) & 0x1FL)]);
282        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 10) & 0x1FL)]);
283        buffer.append(BASE32_ALPHABET[(int) ((longValue >> 5) & 0x1FL)]);
284        buffer.append(BASE32_ALPHABET[(int) (longValue & 0x1FL)]);
285      }
286
287      switch ((off+length) - pos)
288      {
289        case 1:
290          long longValue = ((data[pos] & 0xFFL) << 32);
291          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
292          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
293          buffer.append("======");
294          return;
295
296        case 2:
297          longValue = (((data[pos++] & 0xFFL) << 32) |
298                       ((data[pos] & 0xFFL) << 24));
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("====");
304          return;
305
306        case 3:
307          longValue = (((data[pos++] & 0xFFL) << 32) |
308                       ((data[pos++] & 0xFFL) << 24) |
309                       ((data[pos] & 0xFFL) << 16));
310          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
311          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
312          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 25) & 0x1FL)]);
313          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 20) & 0x1FL)]);
314          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 15) & 0x1FL)]);
315          buffer.append("===");
316          return;
317
318        case 4:
319          longValue = (((data[pos++] & 0xFFL) << 32) |
320                       ((data[pos++] & 0xFFL) << 24) |
321                       ((data[pos++] & 0xFFL) << 16) |
322                       ((data[pos] & 0xFFL) << 8));
323          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 35) & 0x1FL)]);
324          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 30) & 0x1FL)]);
325          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 25) & 0x1FL)]);
326          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 20) & 0x1FL)]);
327          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 15) & 0x1FL)]);
328          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 10) & 0x1FL)]);
329          buffer.append(BASE32_ALPHABET[(int) ((longValue >> 5) & 0x1FL)]);
330          buffer.append("=");
331          return;
332      }
333    }
334    catch (final IOException ioe)
335    {
336      Debug.debugException(ioe);
337
338      // This should never happen.
339      throw new RuntimeException(ioe.getMessage(), ioe);
340    }
341  }
342
343
344
345  /**
346   * Decodes the contents of the provided base32-encoded string.
347   *
348   * @param  data  The base32-encoded string to decode.  It must not be
349   *               {@code null}.
350   *
351   * @return  A byte array containing the decoded data.
352   *
353   * @throws  ParseException  If the contents of the provided string cannot be
354   *                          parsed as base32-encoded data.
355   */
356  @NotNull()
357  public static byte[] decode(@NotNull final String data)
358         throws ParseException
359  {
360    Validator.ensureNotNull(data);
361
362    final int length = data.length();
363    if (length == 0)
364    {
365      return StaticUtils.NO_BYTES;
366    }
367
368    if ((length % 8) != 0)
369    {
370      throw new ParseException(ERR_BASE32_DECODE_INVALID_LENGTH.get(), length);
371    }
372
373    final ByteStringBuffer buffer = new ByteStringBuffer(5 * (length / 8));
374
375    int stringPos = 0;
376    while (stringPos < length)
377    {
378      long longValue = 0x00;
379      for (int i=0; i < 8; i++)
380      {
381        longValue <<= 5;
382        switch (data.charAt(stringPos++))
383        {
384          case 'A':
385          case 'a':
386            longValue |= 0x00L;
387            break;
388          case 'B':
389          case 'b':
390            longValue |= 0x01L;
391            break;
392          case 'C':
393          case 'c':
394            longValue |= 0x02L;
395            break;
396          case 'D':
397          case 'd':
398            longValue |= 0x03L;
399            break;
400          case 'E':
401          case 'e':
402            longValue |= 0x04L;
403            break;
404          case 'F':
405          case 'f':
406            longValue |= 0x05L;
407            break;
408          case 'G':
409          case 'g':
410            longValue |= 0x06L;
411            break;
412          case 'H':
413          case 'h':
414            longValue |= 0x07L;
415            break;
416          case 'I':
417          case 'i':
418            longValue |= 0x08L;
419            break;
420          case 'J':
421          case 'j':
422            longValue |= 0x09L;
423            break;
424          case 'K':
425          case 'k':
426            longValue |= 0x0AL;
427            break;
428          case 'L':
429          case 'l':
430            longValue |= 0x0BL;
431            break;
432          case 'M':
433          case 'm':
434            longValue |= 0x0CL;
435            break;
436          case 'N':
437          case 'n':
438            longValue |= 0x0DL;
439            break;
440          case 'O':
441          case 'o':
442            longValue |= 0x0EL;
443            break;
444          case 'P':
445          case 'p':
446            longValue |= 0x0FL;
447            break;
448          case 'Q':
449          case 'q':
450            longValue |= 0x10L;
451            break;
452          case 'R':
453          case 'r':
454            longValue |= 0x11L;
455            break;
456          case 'S':
457          case 's':
458            longValue |= 0x12L;
459            break;
460          case 'T':
461          case 't':
462            longValue |= 0x13L;
463            break;
464          case 'U':
465          case 'u':
466            longValue |= 0x14L;
467            break;
468          case 'V':
469          case 'v':
470            longValue |= 0x15L;
471            break;
472          case 'W':
473          case 'w':
474            longValue |= 0x16L;
475            break;
476          case 'X':
477          case 'x':
478            longValue |= 0x17L;
479            break;
480          case 'Y':
481          case 'y':
482            longValue |= 0x18L;
483            break;
484          case 'Z':
485          case 'z':
486            longValue |= 0x19L;
487            break;
488          case '2':
489            longValue |= 0x1AL;
490            break;
491          case '3':
492            longValue |= 0x1BL;
493            break;
494          case '4':
495            longValue |= 0x1CL;
496            break;
497          case '5':
498            longValue |= 0x1DL;
499            break;
500          case '6':
501            longValue |= 0x1EL;
502            break;
503          case '7':
504            longValue |= 0x1FL;
505            break;
506
507          case '=':
508            switch (length - stringPos)
509            {
510              case 0:
511                // The string ended with a single equal sign, so there are
512                // four bytes left.
513                buffer.append((byte) ((longValue >> 32) & 0xFFL));
514                buffer.append((byte) ((longValue >> 24) & 0xFFL));
515                buffer.append((byte) ((longValue >> 16) & 0xFFL));
516                buffer.append((byte) ((longValue >> 8) & 0xFFL));
517                return buffer.toByteArray();
518
519              case 2:
520                // The string ended with three equal signs, so there are three
521                // bytes left.
522                longValue <<= 10;
523                buffer.append((byte) ((longValue >> 32) & 0xFFL));
524                buffer.append((byte) ((longValue >> 24) & 0xFFL));
525                buffer.append((byte) ((longValue >> 16) & 0xFFL));
526                return buffer.toByteArray();
527
528              case 3:
529                // The string ended with four equal signs, so there are two
530                // bytes left.
531                longValue <<= 15;
532                buffer.append((byte) ((longValue >> 32) & 0xFFL));
533                buffer.append((byte) ((longValue >> 24) & 0xFFL));
534                return buffer.toByteArray();
535
536              case 5:
537                // The string ended with six equal signs, so there is one byte
538                // left.
539                longValue <<= 25;
540                buffer.append((byte) ((longValue >> 32) & 0xFFL));
541                return buffer.toByteArray();
542
543              default:
544                throw new ParseException(
545                     ERR_BASE32_DECODE_UNEXPECTED_EQUAL.get((stringPos-1)),
546                     (stringPos-1));
547            }
548
549          default:
550            throw new ParseException(
551                 ERR_BASE32_DECODE_UNEXPECTED_CHAR.get(
552                      data.charAt(stringPos-1)),
553                 (stringPos-1));
554        }
555      }
556
557      buffer.append((byte) ((longValue >> 32) & 0xFFL));
558      buffer.append((byte) ((longValue >> 24) & 0xFFL));
559      buffer.append((byte) ((longValue >> 16) & 0xFFL));
560      buffer.append((byte) ((longValue >> 8) & 0xFFL));
561      buffer.append((byte) (longValue & 0xFFL));
562    }
563
564    return buffer.toByteArray();
565  }
566
567
568
569  /**
570   * Decodes the contents of the provided base32-encoded string to a string
571   * containing the raw data using the UTF-8 encoding.
572   *
573   * @param  data  The base32-encoded string to decode.  It must not be
574   *               {@code null}.
575   *
576   * @return  A string containing the decoded data.
577   *
578   * @throws  ParseException  If the contents of the provided string cannot be
579   *                          parsed as base32-encoded data using the UTF-8
580   *                          encoding.
581   */
582  @NotNull()
583  public static String decodeToString(@NotNull final String data)
584         throws ParseException
585  {
586    Validator.ensureNotNull(data);
587
588    final byte[] decodedBytes = decode(data);
589    return StaticUtils.toUTF8String(decodedBytes);
590  }
591}