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}