1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package org.archive.util;
26
27 /***
28 * Base32 - encodes and decodes RFC3548 Base32
29 * (see http://www.faqs.org/rfcs/rfc3548.html )
30 *
31 * Imported public-domain code of Bitzi.
32 *
33 * @author Robert Kaye
34 * @author Gordon Mohr
35 */
36 public class Base32 {
37 private static final String base32Chars =
38 "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
39 private static final int[] base32Lookup =
40 { 0xFF,0xFF,0x1A,0x1B,0x1C,0x1D,0x1E,0x1F,
41 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
42 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,
43 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
44 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
45 0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF,
46 0xFF,0x00,0x01,0x02,0x03,0x04,0x05,0x06,
47 0x07,0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,
48 0x0F,0x10,0x11,0x12,0x13,0x14,0x15,0x16,
49 0x17,0x18,0x19,0xFF,0xFF,0xFF,0xFF,0xFF
50 };
51
52 /***
53 * Encodes byte array to Base32 String.
54 *
55 * @param bytes Bytes to encode.
56 * @return Encoded byte array <code>bytes</code> as a String.
57 *
58 */
59 static public String encode(final byte[] bytes) {
60 int i = 0, index = 0, digit = 0;
61 int currByte, nextByte;
62 StringBuffer base32 = new StringBuffer((bytes.length + 7) * 8 / 5);
63
64 while (i < bytes.length) {
65 currByte = (bytes[i] >= 0) ? bytes[i] : (bytes[i] + 256);
66
67
68 if (index > 3) {
69 if ((i + 1) < bytes.length) {
70 nextByte =
71 (bytes[i + 1] >= 0) ? bytes[i + 1] : (bytes[i + 1] + 256);
72 } else {
73 nextByte = 0;
74 }
75
76 digit = currByte & (0xFF >> index);
77 index = (index + 5) % 8;
78 digit <<= index;
79 digit |= nextByte >> (8 - index);
80 i++;
81 } else {
82 digit = (currByte >> (8 - (index + 5))) & 0x1F;
83 index = (index + 5) % 8;
84 if (index == 0)
85 i++;
86 }
87 base32.append(base32Chars.charAt(digit));
88 }
89
90 return base32.toString();
91 }
92
93 /***
94 * Decodes the given Base32 String to a raw byte array.
95 *
96 * @param base32
97 * @return Decoded <code>base32</code> String as a raw byte array.
98 */
99 static public byte[] decode(final String base32) {
100 int i, index, lookup, offset, digit;
101 byte[] bytes = new byte[base32.length() * 5 / 8];
102
103 for (i = 0, index = 0, offset = 0; i < base32.length(); i++) {
104 lookup = base32.charAt(i) - '0';
105
106
107 if (lookup < 0 || lookup >= base32Lookup.length) {
108 continue;
109 }
110
111 digit = base32Lookup[lookup];
112
113
114 if (digit == 0xFF) {
115 continue;
116 }
117
118 if (index <= 3) {
119 index = (index + 5) % 8;
120 if (index == 0) {
121 bytes[offset] |= digit;
122 offset++;
123 if (offset >= bytes.length)
124 break;
125 } else {
126 bytes[offset] |= digit << (8 - index);
127 }
128 } else {
129 index = (index + 5) % 8;
130 bytes[offset] |= (digit >>> index);
131 offset++;
132
133 if (offset >= bytes.length) {
134 break;
135 }
136 bytes[offset] |= digit << (8 - index);
137 }
138 }
139 return bytes;
140 }
141
142 /*** For testing, take a command-line argument in Base32, decode, print in hex,
143 * encode, print
144 *
145 * @param args
146 */
147 static public void main(String[] args) {
148 if (args.length == 0) {
149 System.out.println("Supply a Base32-encoded argument.");
150 return;
151 }
152 System.out.println(" Original: " + args[0]);
153 byte[] decoded = Base32.decode(args[0]);
154 System.out.print(" Hex: ");
155 for (int i = 0; i < decoded.length; i++) {
156 int b = decoded[i];
157 if (b < 0) {
158 b += 256;
159 }
160 System.out.print((Integer.toHexString(b + 256)).substring(1));
161 }
162 System.out.println();
163 System.out.println("Reencoded: " + Base32.encode(decoded));
164 }
165 }