1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.commons.codec.binary;
19
20 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
21 import static org.junit.jupiter.api.Assertions.assertEquals;
22 import static org.junit.jupiter.api.Assertions.assertFalse;
23 import static org.junit.jupiter.api.Assertions.assertNotNull;
24 import static org.junit.jupiter.api.Assertions.assertNull;
25 import static org.junit.jupiter.api.Assertions.assertThrows;
26 import static org.junit.jupiter.api.Assertions.assertTrue;
27 import static org.junit.jupiter.api.Assertions.fail;
28
29 import java.nio.charset.Charset;
30 import java.nio.charset.StandardCharsets;
31 import java.util.Arrays;
32
33 import org.apache.commons.codec.CodecPolicy;
34 import org.apache.commons.codec.DecoderException;
35 import org.apache.commons.lang3.ArrayUtils;
36 import org.junit.jupiter.api.Test;
37
38 public class Base32Test {
39
40 private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;
41
42
43
44 private static final String [][] BASE32_TEST_CASES = {
45 { "" , "" },
46 { "f" , "MY======" },
47 { "fo" , "MZXQ====" },
48 { "foo" , "MZXW6===" },
49 { "foob" , "MZXW6YQ=" },
50 { "fooba" , "MZXW6YTB" },
51 { "foobar" , "MZXW6YTBOI======" }
52 };
53
54
55
56
57
58
59
60 static final String[] BASE32_IMPOSSIBLE_CASES = {
61 "MC======",
62 "MZXE====",
63 "MZXWB===",
64 "MZXW6YB=",
65 "MZXW6YTBOC======",
66 "AB======"
67 };
68
69
70
71 private static final String[] BASE32_IMPOSSIBLE_CASES_CHUNKED = {
72 "M2======\r\n",
73 "MZX0====\r\n",
74 "MZXW0===\r\n",
75 "MZXW6Y2=\r\n",
76 "MZXW6YTBO2======\r\n"
77 };
78
79
80
81 private static final String[] BASE32HEX_IMPOSSIBLE_CASES = {
82 "C2======",
83 "CPN4====",
84 "CPNM1===",
85 "CPNMUO1=",
86 "CPNMUOJ1E2======"
87 };
88
89
90
91
92
93
94
95 private static final byte[] ENCODE_TABLE = {
96 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
97 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
98 '2', '3', '4', '5', '6', '7'
99 };
100
101
102 private static final Object[][] BASE32_BINARY_TEST_CASES;
103
104
105
106
107 static {
108 final Hex hex = new Hex();
109 try {
110 BASE32_BINARY_TEST_CASES = new Object[][] {
111 new Object[] { hex.decode("623a01735836e9a126e12fbf95e013ee6892997c"),
112 "MI5AC42YG3U2CJXBF67ZLYAT5ZUJFGL4" },
113 new Object[] { hex.decode("623a01735836e9a126e12fbf95e013ee6892997c"),
114 "mi5ac42yg3u2cjxbf67zlyat5zujfgl4" },
115 new Object[] { hex.decode("739ce42108"),
116 "OOOOIIII" }
117 };
118 } catch (final DecoderException de) {
119 throw new AssertionError(":(", de);
120 }
121 }
122
123
124 private static final String [][] BASE32HEX_TEST_CASES = {
125 { "" , "" },
126 { "f" , "CO======" },
127 { "fo" , "CPNG====" },
128 { "foo" , "CPNMU===" },
129 { "foob" , "CPNMUOG=" },
130 { "fooba" , "CPNMUOJ1" },
131 { "foobar" , "CPNMUOJ1E8======" }
132 };
133
134
135
136 private static final String [][] BASE32_TEST_CASES_CHUNKED = {
137 { "" , "" },
138 { "f" , "MY======\r\n" },
139 { "fo" , "MZXQ====\r\n" },
140 { "foo" , "MZXW6===\r\n" },
141 { "foob" , "MZXW6YQ=\r\n" },
142 { "fooba" , "MZXW6YTB\r\n" },
143 { "foobar" , "MZXW6YTBOI======\r\n" }
144 };
145
146
147
148 private static final String [][] BASE32_PAD_TEST_CASES = {
149 { "" , "" },
150 { "f" , "MY%%%%%%" },
151 { "fo" , "MZXQ%%%%" },
152 { "foo" , "MZXW6%%%" },
153 { "foob" , "MZXW6YQ%" },
154 { "fooba" , "MZXW6YTB" },
155 { "foobar" , "MZXW6YTBOI%%%%%%" }
156 };
157
158
159
160
161
162
163
164
165
166
167 private static void assertBase32DecodingOfTrailingBits(final int nbits) {
168
169 final Base32 codec = new Base32(0, null, false, BaseNCodec.PAD_DEFAULT, CodecPolicy.STRICT);
170 assertTrue(codec.isStrictDecoding());
171 assertEquals(CodecPolicy.STRICT, codec.getCodecPolicy());
172
173 final Base32 defaultCodec = new Base32();
174 assertFalse(defaultCodec.isStrictDecoding());
175 assertEquals(CodecPolicy.LENIENT, defaultCodec.getCodecPolicy());
176
177
178
179 final int length = nbits / 5;
180 final byte[] encoded = new byte[8];
181 Arrays.fill(encoded, 0, length, ENCODE_TABLE[0]);
182 Arrays.fill(encoded, length, encoded.length, (byte) '=');
183
184 final int discard = nbits % 8;
185 final int emptyBitsMask = (1 << discard) - 1;
186
187 final boolean invalid = length == 1 || length == 3 || length == 6;
188
189 final int last = length - 1;
190 for (int i = 0; i < 32; i++) {
191 encoded[last] = ENCODE_TABLE[i];
192
193
194 if (invalid || (i & emptyBitsMask) != 0) {
195 assertThrows(IllegalArgumentException.class, () -> codec.decode(encoded), "Final base-32 digit should not be allowed");
196
197 final byte[] decoded = defaultCodec.decode(encoded);
198
199 assertFalse(Arrays.equals(encoded, defaultCodec.encode(decoded)));
200 } else {
201
202 final byte[] decoded = codec.decode(encoded);
203
204 final int bitsEncoded = i >> discard;
205 assertEquals(bitsEncoded, decoded[decoded.length - 1], "Invalid decoding of last character");
206
207 assertArrayEquals(encoded, codec.encode(decoded));
208 }
209 }
210 }
211
212 @Test
213 public void testBase32AtBufferEnd() {
214 testBase32InBuffer(100, 0);
215 }
216
217 @Test
218 public void testBase32AtBufferMiddle() {
219 testBase32InBuffer(100, 100);
220 }
221
222 @Test
223 public void testBase32AtBufferStart() {
224 testBase32InBuffer(0, 100);
225 }
226
227 @Test
228 public void testBase32BinarySamples() throws Exception {
229 final Base32 codec = new Base32();
230 for (final Object[] element : BASE32_BINARY_TEST_CASES) {
231 final String expected;
232 if (element.length > 2) {
233 expected = (String) element[2];
234 } else {
235 expected = (String) element[1];
236 }
237 assertEquals(expected.toUpperCase(), codec.encodeAsString((byte[]) element[0]));
238 }
239 }
240
241 @Test
242 public void testBase32BinarySamplesReverse() throws Exception {
243 final Base32 codec = new Base32();
244 for (final Object[] element : BASE32_BINARY_TEST_CASES) {
245 assertArrayEquals((byte[]) element[0], codec.decode((String) element[1]));
246 }
247 }
248
249 @Test
250 public void testBase32Chunked() throws Exception {
251 final Base32 codec = new Base32(20);
252 for (final String[] element : BASE32_TEST_CASES_CHUNKED) {
253 assertEquals(element[1], codec.encodeAsString(element[0].getBytes(CHARSET_UTF8)));
254 }
255 }
256
257 @Test
258 public void testBase32DecodingOfTrailing10Bits() {
259 assertBase32DecodingOfTrailingBits(10);
260 }
261
262 @Test
263 public void testBase32DecodingOfTrailing15Bits() {
264 assertBase32DecodingOfTrailingBits(15);
265 }
266
267 @Test
268 public void testBase32DecodingOfTrailing20Bits() {
269 assertBase32DecodingOfTrailingBits(20);
270 }
271
272 @Test
273 public void testBase32DecodingOfTrailing25Bits() {
274 assertBase32DecodingOfTrailingBits(25);
275 }
276
277 @Test
278 public void testBase32DecodingOfTrailing30Bits() {
279 assertBase32DecodingOfTrailingBits(30);
280 }
281
282 @Test
283 public void testBase32DecodingOfTrailing35Bits() {
284 assertBase32DecodingOfTrailingBits(35);
285 }
286
287 @Test
288 public void testBase32DecodingOfTrailing5Bits() {
289 assertBase32DecodingOfTrailingBits(5);
290 }
291
292 @Test
293 public void testBase32HexImpossibleSamples() {
294 testImpossibleCases(new Base32(0, null, true, BaseNCodec.PAD_DEFAULT, CodecPolicy.STRICT), BASE32HEX_IMPOSSIBLE_CASES);
295 }
296
297 @Test
298 public void testBase32HexSamples() throws Exception {
299 final Base32 codec = new Base32(true);
300 for (final String[] element : BASE32HEX_TEST_CASES) {
301 assertEquals(element[1], codec.encodeAsString(element[0].getBytes(CHARSET_UTF8)));
302 }
303 }
304
305 @Test
306 public void testBase32HexSamplesReverse() throws Exception {
307 final Base32 codec = new Base32(true);
308 for (final String[] element : BASE32HEX_TEST_CASES) {
309 assertEquals(element[0], new String(codec.decode(element[1]), CHARSET_UTF8));
310 }
311 }
312
313 @Test
314 public void testBase32HexSamplesReverseLowercase() throws Exception {
315 final Base32 codec = new Base32(true);
316 for (final String[] element : BASE32HEX_TEST_CASES) {
317 assertEquals(element[0], new String(codec.decode(element[1].toLowerCase()), CHARSET_UTF8));
318 }
319 }
320
321 @Test
322 public void testBase32ImpossibleChunked() {
323 testImpossibleCases(new Base32(20, BaseNCodec.CHUNK_SEPARATOR, false, BaseNCodec.PAD_DEFAULT, CodecPolicy.STRICT), BASE32_IMPOSSIBLE_CASES_CHUNKED);
324 }
325
326 @Test
327 public void testBase32ImpossibleSamples() {
328 testImpossibleCases(new Base32(0, null, false, BaseNCodec.PAD_DEFAULT, CodecPolicy.STRICT), BASE32_IMPOSSIBLE_CASES);
329 }
330
331 private void testBase32InBuffer(final int startPasSize, final int endPadSize) {
332 final Base32 codec = new Base32();
333 for (final String[] element : BASE32_TEST_CASES) {
334 final byte[] bytes = element[0].getBytes(CHARSET_UTF8);
335 byte[] buffer = ArrayUtils.addAll(bytes, new byte[endPadSize]);
336 buffer = ArrayUtils.addAll(new byte[startPasSize], buffer);
337 assertEquals(element[1], StringUtils.newStringUtf8(codec.encode(buffer, startPasSize, bytes.length)));
338 }
339 }
340
341 @Test
342 public void testBase32Samples() throws Exception {
343 final Base32 codec = new Base32();
344 for (final String[] element : BASE32_TEST_CASES) {
345 assertEquals(element[1], codec.encodeAsString(element[0].getBytes(CHARSET_UTF8)));
346 }
347 }
348
349 @Test
350 public void testBase32SamplesNonDefaultPadding() throws Exception {
351 final Base32 codec = new Base32((byte) 0x25);
352
353 for (final String[] element : BASE32_PAD_TEST_CASES) {
354 assertEquals(element[1], codec.encodeAsString(element[0].getBytes(CHARSET_UTF8)));
355 }
356 }
357
358 @Test
359 public void testCodec200() {
360 final Base32 codec = new Base32(true, (byte) 'W');
361 assertNotNull(codec);
362 }
363
364 @Test
365 public void testConstructors() {
366 Base32 base32;
367 base32 = new Base32();
368 base32 = new Base32(-1);
369 base32 = new Base32(-1, new byte[] {});
370 base32 = new Base32(32, new byte[] {});
371 base32 = new Base32(32, new byte[] {}, false);
372
373
374 base32 = new Base32(-1, new byte[] { 'A' });
375 base32 = new Base32(32, new byte[] { '$' });
376 assertThrows(IllegalArgumentException.class, () -> new Base32(32, null), "null line separator");
377 assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { 'A' }), "'A' as a line separator");
378 assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { '=' }), "'=' as a line separator");
379 assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { 'A', '$' }), "'A$' as a line separator");
380 assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { '\n' }, false, (byte) 'A'), "'A' as padding");
381 assertThrows(IllegalArgumentException.class, () -> new Base32(32, new byte[] { '\n' }, false, (byte) ' '), "' ' as padding");
382
383 base32 = new Base32(32, new byte[] { ' ', '$', '\n', '\r', '\t' });
384 assertNotNull(base32);
385 }
386
387
388
389
390 @Test
391 public void testEmptyBase32() {
392 byte[] empty = {};
393 byte[] result = new Base32().encode(empty);
394 assertEquals(0, result.length, "empty Base32 encode");
395 assertNull(new Base32().encode(null), "empty Base32 encode");
396 result = new Base32().encode(empty, 0, 1);
397 assertEquals(0, result.length, "empty Base32 encode with offset");
398 assertNull(new Base32().encode(null), "empty Base32 encode with offset");
399
400 empty = new byte[0];
401 result = new Base32().decode(empty);
402 assertEquals(0, result.length, "empty Base32 decode");
403 assertNull(new Base32().decode((byte[]) null), "empty Base32 encode");
404 }
405
406 private void testImpossibleCases(final Base32 codec, final String[] impossible_cases) {
407 for (final String impossible : impossible_cases) {
408 assertThrows(IllegalArgumentException.class, () -> codec.decode(impossible));
409 }
410 }
411
412 @Test
413 public void testIsInAlphabet() {
414
415 Base32 b32 = new Base32(true);
416 assertFalse(b32.isInAlphabet((byte) 0));
417 assertFalse(b32.isInAlphabet((byte) 1));
418 assertFalse(b32.isInAlphabet((byte) -1));
419 assertFalse(b32.isInAlphabet((byte) -15));
420 assertFalse(b32.isInAlphabet((byte) -32));
421 assertFalse(b32.isInAlphabet((byte) 127));
422 assertFalse(b32.isInAlphabet((byte) 128));
423 assertFalse(b32.isInAlphabet((byte) 255));
424
425
426 b32 = new Base32(false);
427 for (char c = '2'; c <= '7'; c++) {
428 assertTrue(b32.isInAlphabet((byte) c));
429 }
430 for (char c = 'A'; c <= 'Z'; c++) {
431 assertTrue(b32.isInAlphabet((byte) c));
432 }
433 for (char c = 'a'; c <= 'z'; c++) {
434 assertTrue(b32.isInAlphabet((byte) c));
435 }
436 assertFalse(b32.isInAlphabet((byte) '1'));
437 assertFalse(b32.isInAlphabet((byte) '8'));
438 assertFalse(b32.isInAlphabet((byte) ('A' - 1)));
439 assertFalse(b32.isInAlphabet((byte) ('Z' + 1)));
440
441
442 b32 = new Base32(true);
443 for (char c = '0'; c <= '9'; c++) {
444 assertTrue(b32.isInAlphabet((byte) c));
445 }
446 for (char c = 'A'; c <= 'V'; c++) {
447 assertTrue(b32.isInAlphabet((byte) c));
448 }
449 for (char c = 'a'; c <= 'v'; c++) {
450 assertTrue(b32.isInAlphabet((byte) c));
451 }
452 assertFalse(b32.isInAlphabet((byte) ('0' - 1)));
453 assertFalse(b32.isInAlphabet((byte) ('9' + 1)));
454 assertFalse(b32.isInAlphabet((byte) ('A' - 1)));
455 assertFalse(b32.isInAlphabet((byte) ('V' + 1)));
456 assertFalse(b32.isInAlphabet((byte) ('a' - 1)));
457 assertFalse(b32.isInAlphabet((byte) ('v' + 1)));
458 }
459
460 @Test
461 public void testRandomBytes() {
462 for (int i = 0; i < 20; i++) {
463 final Base32 codec = new Base32();
464 final byte[][] b = BaseNTestData.randomData(codec, i);
465 assertEquals(b[1].length, codec.getEncodedLength(b[0]), i + " " + codec.lineLength);
466
467 }
468 }
469
470 @Test
471 public void testRandomBytesChunked() {
472 for (int i = 0; i < 20; i++) {
473 final Base32 codec = new Base32(10);
474 final byte[][] b = BaseNTestData.randomData(codec, i);
475 assertEquals(b[1].length, codec.getEncodedLength(b[0]), i + " " + codec.lineLength);
476
477 }
478 }
479
480 @Test
481 public void testRandomBytesHex() {
482 for (int i = 0; i < 20; i++) {
483 final Base32 codec = new Base32(true);
484 final byte[][] b = BaseNTestData.randomData(codec, i);
485 assertEquals(b[1].length, codec.getEncodedLength(b[0]), i + " " + codec.lineLength);
486
487 }
488 }
489
490 @Test
491 public void testSingleCharEncoding() {
492 for (int i = 0; i < 20; i++) {
493 Base32 codec = new Base32();
494 final BaseNCodec.Context context = new BaseNCodec.Context();
495 final byte[] unencoded = new byte[i];
496 final byte[] allInOne = codec.encode(unencoded);
497 codec = new Base32();
498 for (int j = 0; j < unencoded.length; j++) {
499 codec.encode(unencoded, j, 1, context);
500 }
501 codec.encode(unencoded, 0, -1, context);
502 final byte[] singly = new byte[allInOne.length];
503 codec.readResults(singly, 0, 100, context);
504 if (!Arrays.equals(allInOne, singly)) {
505 fail();
506 }
507 }
508 }
509 }