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}