View Javadoc

1   /*
2    * Licensed to the University Corporation for Advanced Internet Development, 
3    * Inc. (UCAID) under one or more contributor license agreements.  See the 
4    * NOTICE file distributed with this work for additional information regarding
5    * copyright ownership. The UCAID licenses this file to You under the Apache 
6    * License, Version 2.0 (the "License"); you may not use this file except in 
7    * compliance with the License.  You may obtain a copy of the License at
8    *
9    *    http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package edu.internet2.middleware.shibboleth.common.util;
19  
20  /**
21   * Base32 - encodes and decodes 'Canonical' Base32
22   *
23   * @author  Robert Kaye & Gordon Mohr
24   */
25  public class Base32 {
26  
27  	/* lookup table used to encode() groups of 5 bits of data */
28  	private static final String base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
29  
30  	/* lookup table used to decode() characters in Base32 strings */
31  	private static final byte[] base32Lookup =
32  		{ 26,27,28,29,30,31,-1,-1,-1,-1,-1,-1,-1,-1,       //   23456789:;<=>?
33  		  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, // @ABCDEFGHIJKLMNO
34  	 	  15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, // PQRSTUVWXYZ[\]^_
35  		  -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, // `abcdefghijklmno
36  		  15,16,17,18,19,20,21,22,23,24,25                 // pqrstuvwxyz
37      		};
38  
39  	/* Messsages for Illegal Parameter Exceptions in decode() */
40  	private static final String errorCanonicalLength = "non canonical Base32 string length";
41  	private static final String errorCanonicalEnd    = "non canonical bits at end of Base32 string";
42  	private static final String errorInvalidChar     = "invalid character in Base32 string";
43  
44  	/**
45  	 * Encode an array of binary bytes into a Base32 string.
46  	 * Should not fail (the only possible exception is that the
47  	 * returned string cannot be allocated in memory)
48  	 */    
49  	static public String encode(final byte[] bytes) {
50      
51  		StringBuffer base32 = new StringBuffer((bytes.length * 8 + 4) / 5);
52  		int currByte, digit, i = 0;
53  
54  		while (i < bytes.length) {
55  		
56  			// INVARIANTS FOR EACH STEP n in [0..5[; digit in [0..31[; 
57  			// The remaining n bits are already aligned on top positions
58  			// of the 5 least bits of digit, the other bits are 0.
59  			
60  			// STEP n = 0; insert new 5 bits, leave 3 bits
61  			currByte = bytes[i++] & 255;
62  			base32.append(base32Chars.charAt(currByte >> 3));
63  			digit = (currByte & 7) << 2;
64  			if (i >= bytes.length) { // put the last 3 bits
65  				base32.append(base32Chars.charAt(digit));
66  				break;
67  			}
68              
69  
70  			// STEP n = 3: insert 2 new bits, then 5 bits, leave 1 bit
71  			currByte = bytes[i++] & 255;
72  			base32.append(base32Chars.charAt(digit | (currByte >> 6)));
73  			base32.append(base32Chars.charAt((currByte >> 1) & 31));
74  			digit = (currByte & 1) << 4;
75  			if (i >= bytes.length) { // put the last 1 bit
76  				base32.append(base32Chars.charAt(digit));
77  				break;
78  			}
79  
80  			// STEP n = 1: insert 4 new bits, leave 4 bit
81  			currByte = bytes[i++] & 255;
82  			base32.append(base32Chars.charAt(digit | (currByte >> 4)));
83  			digit = (currByte & 15) << 1;
84  			if (i >= bytes.length) { // put the last 4 bits
85  				base32.append(base32Chars.charAt(digit));
86  				break;
87  			}
88  
89  			// STEP n = 4: insert 1 new bit, then 5 bits, leave 2 bits
90  			currByte = bytes[i++] & 255;
91  			base32.append(base32Chars.charAt(digit | (currByte >> 7)));
92  			base32.append(base32Chars.charAt((currByte >> 2) & 31));
93  			digit = (currByte & 3) << 3;
94  			if (i >= bytes.length) { // put the last 2 bits
95  				base32.append(base32Chars.charAt(digit));
96  				break;
97  			}
98  
99  			// STEP n = 2: insert 3 new bits, then 5 bits, leave 0 bit
100 			currByte = bytes[i++] & 255;
101 			base32.append(base32Chars.charAt(digit | (currByte >> 5)));
102 			base32.append(base32Chars.charAt(currByte & 31));
103 			//// This point is reached for bytes.length multiple of 5
104 		}
105 	
106 		return base32.toString();
107 	}
108 
109    
110 	/**
111 	 * Decode a Base32 string into an array of binary bytes.
112 	 * May fail if the parameter is a non canonical Base32 string	
113 	 * (the only other possible exception is that the
114 	 * returned array cannot be allocated in memory)
115 	 */    
116 	static public byte[] decode(final String base32) throws IllegalArgumentException {
117 
118 	// Note that the code below detects could detect non canonical
119 	// Base32 length within the loop. However canonical Base32 length
120 	// can be tested before entering the loop.
121 	// A canonical Base32 length modulo 8 cannot be:
122 	// 1 (aborts discarding 5 bits at STEP n=0 which produces no byte),
123 	// 3 (aborts discarding 7 bits at STEP n=2 which produces no byte),
124 	// 6 (aborts discarding 6 bits at STEP n=1 which produces no byte)
125 	// So these tests could be avoided within the loop.
126 	switch (base32.length() % 8) { // test the length of last subblock
127 
128 		case 1: //  5 bits in subblock:  0 useful bits but 5 discarded
129 		case 3: // 15 bits in subblock:  8 useful bits but 7 discarded
130 		case 6: // 30 bits in subblock: 24 useful bits but 6 discarded
131           
132 		throw new IllegalArgumentException(errorCanonicalLength);
133 	}
134 
135 	byte[] bytes = new byte[base32.length() * 5 / 8];
136 	int offset = 0, i = 0, lookup;
137 	byte nextByte, digit;
138 
139 	// Also the code below does test that other discarded bits
140 	// (1 to 4 bits at end) are effectively 0.
141 	while (i < base32.length()) {
142 		// Read the 1st char in a 8-chars subblock
143 		// check that chars are not outside the lookup table and valid
144 		lookup = base32.charAt(i++) - '2';
145 		if (lookup < 0 || lookup >= base32Lookup.length) {
146 			throw new IllegalArgumentException(errorInvalidChar);
147 		}
148 		digit = base32Lookup[lookup];
149 		if (digit == -1) {
150 			throw new IllegalArgumentException(errorInvalidChar);
151 		}
152 
153 		// STEP n = 0: leave 5 bits
154 		nextByte = (byte)(digit << 3);
155 		// Assert(i < base32.length) // tested before loop
156 		// Read the 2nd char in a 8-chars subblock
157 		// Check that chars are not outside the lookup table and valid
158 		lookup = base32.charAt(i++) - '2';
159 		if (lookup < 0 || lookup >= base32Lookup.length) {
160 			throw new IllegalArgumentException(errorInvalidChar);
161 		}
162 		digit = base32Lookup[lookup];
163 		if (digit == -1) {
164 			throw new IllegalArgumentException(errorInvalidChar);
165 		}
166 
167 		// STEP n = 5: insert 3 bits, leave 2 bits
168 		bytes[offset++] = (byte)(nextByte | (digit >> 2));
169 		nextByte = (byte)((digit & 3) << 6);
170 		if (i >= base32.length()) {
171 			if (nextByte != (byte)0) {
172 				throw new IllegalArgumentException(errorCanonicalEnd);
173 			}
174 			break; // discard the remaining 2 bits
175 		}
176 
177 		// Read the 3rd char in a 8-chars subblock
178 		// Check that chars are not outside the lookup table and valid
179 		lookup = base32.charAt(i++) - '2';
180 		if (lookup < 0 || lookup >= base32Lookup.length) {
181 			throw new IllegalArgumentException(errorInvalidChar);
182 		}
183 		digit = base32Lookup[lookup];
184 		if (digit == -1) {
185 			throw new IllegalArgumentException(errorInvalidChar);
186 		}
187 
188 		// STEP n = 2: leave 7 bits
189 		nextByte |= (byte)(digit << 1);
190 		// Assert(i < base32.length) // tested before loop
191  		// Read the 4th char in a 8-chars subblock
192 		// Check that chars are not outside the lookup table and valid
193 		lookup = base32.charAt(i++) - '2';
194 		if (lookup < 0 || lookup >= base32Lookup.length) {
195 			throw new IllegalArgumentException(errorInvalidChar);
196 		}
197 		digit = base32Lookup[lookup];
198 		if (digit == -1) {
199 			throw new IllegalArgumentException(errorInvalidChar);
200 		}
201 
202 		// STEP n = 7: insert 1 bit, leave 4 bits
203 		bytes[offset++] = (byte)(nextByte | (digit >> 4));
204 		nextByte = (byte)((digit & 15) << 4);
205 		if (i >= base32.length()) {
206 			if (nextByte != (byte)0) {
207 				throw new IllegalArgumentException(errorCanonicalEnd);
208 			}
209 			break; // discard the remaining 4 bits
210 		}
211 
212 		// Read the 5th char in a 8-chars subblock
213 		// Assert that chars are not outside the lookup table and valid
214 		lookup = base32.charAt(i++) - '2';
215 		if (lookup < 0 || lookup >= base32Lookup.length) {
216 			throw new IllegalArgumentException(errorInvalidChar);
217 		}
218 		digit = base32Lookup[lookup];
219 		if (digit == -1) {
220 			throw new IllegalArgumentException(errorInvalidChar);
221 		}
222 
223 		// STEP n = 4: insert 4 bits, leave 1 bit
224 		bytes[offset++] = (byte)(nextByte | (digit >> 1));
225 		nextByte = (byte)((digit & 1) << 7);
226 		if (i >= base32.length()) {
227 			if (nextByte != (byte)0) {
228 				throw new IllegalArgumentException(errorCanonicalEnd);
229 			}
230 			break; // discard the remaining 1 bit
231 		}
232 
233 		// Read the 6th char in a 8-chars subblock
234 		// Check that chars are not outside the lookup table and valid
235 		lookup = base32.charAt(i++) - '2';
236 		if (lookup < 0 || lookup >= base32Lookup.length) {
237 			throw new IllegalArgumentException(errorInvalidChar);
238 		}
239 		digit = base32Lookup[lookup];
240 		if (digit == -1) {
241 			throw new IllegalArgumentException(errorInvalidChar);
242 		}
243 
244 		// STEP n = 1: leave 6 bits
245 		nextByte |= (byte)(digit << 2);
246 		// Assert(i < base32.length) // tested before loop
247 		// Read the 7th char in a 8-chars subblock
248 		// Check that chars are not outside the lookup table and valid
249 		lookup = base32.charAt(i++) - '2';
250 		if (lookup < 0 || lookup >= base32Lookup.length) {
251 			throw new IllegalArgumentException(errorInvalidChar);
252 		}
253 		digit = base32Lookup[lookup];
254 		if (digit == -1) {
255 			throw new IllegalArgumentException(errorInvalidChar);
256 		}
257 
258 		// STEP n = 6: insert 2 bits, leave 3 bits
259 		bytes[offset++] = (byte)(nextByte | (digit >> 3));
260 		nextByte = (byte)((digit & 7) << 5);
261 		if (i >= base32.length()) {
262 			if (nextByte != (byte)0) {
263 				throw new IllegalArgumentException(errorCanonicalEnd);
264 			}
265 			break; // discard the remaining 3 bits
266 		}
267         
268 		// Read the 8th char in a 8-chars subblock
269 		// Check that chars are not outside the lookup table and valid
270 		lookup = base32.charAt(i++) - '2';
271 		if (lookup < 0 || lookup >= base32Lookup.length) {
272 			throw new IllegalArgumentException(errorInvalidChar);
273 		}
274 		digit = base32Lookup[lookup];
275 		if (digit == -1) {
276 			throw new IllegalArgumentException(errorInvalidChar);
277 		}
278 
279 		// STEP n = 3: insert 5 bits, leave 0 bit
280 		bytes[offset++] = (byte)(nextByte | digit);
281 		// possible end of string here with no trailing bits
282 	}
283 
284 		// On loop exit, discard trialing n bits.
285 		return bytes;
286 	}
287 }
288