/* * JBoss, Home of Professional Open Source. * Copyright 2014 Red Hat, Inc., and individual contributors * as indicated by the @author tags. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.wildfly.security.util; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import java.nio.charset.StandardCharsets; import org.junit.Assert; import org.junit.Test; import org.wildfly.security.util.Alphabet.Base64Alphabet; /** * Tests of encoding/decoding Base64 B (standard alphabet) * implemented in org.wildfly.security.util.Base64 * * @author <a href="mailto:jkalina@redhat.com">Jan Kalina</a> */ public class Base64Test { /* * Standard Base64 alphabet encoding * (Expected values by http://www.freeformatter.com/base64-encoder.html) */ @Test public void testEncodeBlank() { assertEquals("", ByteIterator.EMPTY.base64Encode().drainToString()); } @Test public void testEncodeWithoutPadding() { assertEquals("YWJj", CodePointIterator.ofString("abc").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeWith1Padding() { assertEquals("YWI=", CodePointIterator.ofString("ab").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeWith2Padding() { assertEquals("YWJjZA==", CodePointIterator.ofString("abcd").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeWithTurnedOffPadding() { assertEquals("YWJjZA", CodePointIterator.ofString("abcd").asLatin1().base64Encode(Base64Alphabet.STANDARD, false).drainToString()); } @Test public void testEncodeBinary() { assertEquals("AAEjRWeJq83v", ByteIterator.ofBytes((byte)0x00,(byte)0x01,(byte)0x23,(byte)0x45,(byte)0x67,(byte)0x89,(byte)0xAB,(byte)0xCD,(byte)0xEF).base64Encode().drainToString()); } @Test public void testEncodeRfc1() { assertEquals("Zg==", CodePointIterator.ofString("f").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeRfc2() { assertEquals("Zm8=", CodePointIterator.ofString("fo").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeRfc3() { assertEquals("Zm9v", CodePointIterator.ofString("foo").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeRfc4() { assertEquals("Zm9vYg==", CodePointIterator.ofString("foob").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeRfc5() { assertEquals("Zm9vYmE=", CodePointIterator.ofString("fooba").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeRfc6() { assertEquals("Zm9vYmFy", CodePointIterator.ofString("foobar").asLatin1().base64Encode().drainToString()); } @Test public void testEncodeAgainstPrecomputedValue() throws Exception { final byte[] input = "Testing input of base64 function".getBytes("UTF-8"); final String output = CodePointIterator.ofString("Testing input of base64 function").asLatin1().base64Encode().drainToString(); Assert.assertEquals("VGVzdGluZyBpbnB1dCBvZiBiYXNlNjQgZnVuY3Rpb24=", output); Assert.assertArrayEquals(input, CodePointIterator.ofString(output).base64Decode().drain()); } @Test public void testEncodeByteStartingWithOne() { ByteStringBuilder bsb = new ByteStringBuilder(); bsb.append((byte)0x00); bsb.append((byte)0xB8); assertEquals("ALg=", bsb.iterate().base64Encode().drainToString()); } @Test public void testEncodeMoreBinaryBytes() { ByteStringBuilder bsb = new ByteStringBuilder(); bsb.append((byte)0xD0); bsb.append((byte)0xB8); bsb.append((byte)0xE4); bsb.append((byte)0xBD); bsb.append((byte)0xA0); bsb.append((byte)0xF0); bsb.append((byte)0x9F); bsb.append((byte)0x82); bsb.append((byte)0xA1); bsb.append((byte)0x31); bsb.append((byte)0xE2); bsb.append((byte)0x81); bsb.append((byte)0x84); bsb.append((byte)0x32); bsb.append((byte)0x20); bsb.append((byte)0xCC); bsb.append((byte)0x81); assertEquals("0LjkvaDwn4KhMeKBhDIgzIE=", bsb.iterate().base64Encode().drainToString()); } /* * Standard Base64 alphabet decoding * (Expected values by http://www.freeformatter.com/base64-encoder.html) */ @Test public void testDecodeBlank() throws Exception { Assert.assertArrayEquals(new byte[]{}, CodePointIterator.EMPTY.base64Decode(Base64Alphabet.STANDARD, false).drain()); } @Test public void testDecodeWithoutPadding() throws Exception { assertEquals("abc", CodePointIterator.ofString("YWJj").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeWith1Padding() throws Exception { assertEquals("ab", CodePointIterator.ofString("YWI=").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeWith2Padding() throws Exception { assertEquals("abcd", CodePointIterator.ofString("YWJjZA==").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeBinary() throws Exception { byte[] out = CodePointIterator.ofString("AAEjRWeJq83v").base64Decode(Base64Alphabet.STANDARD, false).drain(); Assert.assertArrayEquals(new byte[]{(byte)0x00,(byte)0x01,(byte)0x23,(byte)0x45,(byte)0x67,(byte)0x89,(byte)0xAB,(byte)0xCD,(byte)0xEF}, out); } @Test public void testDecodeRfc1() throws Exception { assertEquals("f", CodePointIterator.ofString("Zg==").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeRfc2() throws Exception { assertEquals("fo", CodePointIterator.ofString("Zm8=").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeRfc3() throws Exception { assertEquals("foo", CodePointIterator.ofString("Zm9v").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeRfc4() throws Exception { assertEquals("foob", CodePointIterator.ofString("Zm9vYg==").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeRfc5() throws Exception { assertEquals("fooba", CodePointIterator.ofString("Zm9vYmE=").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } @Test public void testDecodeRfc6() throws Exception { assertEquals("foobar", CodePointIterator.ofString("Zm9vYmFy").base64Decode(Base64Alphabet.STANDARD, false).asUtf8String().drainToString()); } /* * Bcrypt Base64 alphabet encoding * (Expected values by php-litesec library - https://github.com/Jacques1/php-litesec/blob/master/src/password_hash.php) */ @Test public void testBcryptEncodeF() { assertEquals("Xe", CodePointIterator.ofString("f").asLatin1().base64Encode(Base64Alphabet.BCRYPT, false).drainToString()); } @Test public void testBcryptEncodeFoobar() { assertEquals("Xk7tWkDw", CodePointIterator.ofString("foobar").asLatin1().base64Encode(Base64Alphabet.BCRYPT, false).drainToString()); } @Test public void testUtf8Transcode() { final String str = "abc123xyz987ä³€¼‘’“”"; final byte[] bytes = str.getBytes(StandardCharsets.UTF_8); assertArrayEquals(bytes, CodePointIterator.ofString(str).asUtf8().drain()); assertEquals(str, ByteIterator.ofBytes(bytes).asUtf8String().drainToString()); assertEquals(str, CodePointIterator.ofString(str).asUtf8().asUtf8String().drainToString()); } @Test public void testBcryptEncodeUnicode() { assertEquals(".DRCm8EGrM85lM89tu", CodePointIterator.ofString("\u0000\u0054\u0123\u1234\uFEDC\uFFFF").asUtf8().base64Encode(Base64Alphabet.BCRYPT, false).drainToString()); } /* * Bcrypt Base64 alphabet decoding * (Expected values by php-litesec library - https://github.com/Jacques1/php-litesec/blob/master/src/password_hash.php) */ @Test public void testBcryptDecodeF() throws Exception { assertEquals("f", CodePointIterator.ofString("Xe").base64Decode(Base64Alphabet.BCRYPT, false).asUtf8String().drainToString()); } @Test public void testBcryptDecodeFoobar() throws Exception { assertEquals("foobar", CodePointIterator.ofString("Xk7tWkDw").base64Decode(Base64Alphabet.BCRYPT, false).asUtf8String().drainToString()); } @Test public void testBcryptDecodeUnicode() throws Exception { String in = ".DRCm8EGrM85lM89tu"; assertArrayEquals(new byte[]{(byte)0x00,(byte)0x54,(byte)0xC4,(byte)0xA3,(byte)0xE1,(byte)0x88,(byte)0xB4,(byte)0xEF,(byte)0xBB,(byte)0x9C,(byte)0xEF,(byte)0xBF,(byte)0xBF}, CodePointIterator.ofString(in).base64Decode(Base64Alphabet.BCRYPT, false).drain()); } /* * ModCrypt Base64 alphabet encoding * (Expected values by https://github.com/magthe/sandi/blob/master/test-src/Codec/Binary/XxTest.hs) */ @Test public void testModCryptEncodeF() { assertEquals("NU", ByteIterator.ofBytes("f".getBytes(StandardCharsets.UTF_8)).base64Encode(Base64Alphabet.MOD_CRYPT, false).drainToString()); } @Test public void testModCryptEncodeFo() { assertEquals("Naw", ByteIterator.ofBytes("fo".getBytes(StandardCharsets.UTF_8)).base64Encode(Base64Alphabet.MOD_CRYPT, false).drainToString()); } @Test public void testModCryptEncodeFoo() { assertEquals("Naxj", ByteIterator.ofBytes("foo".getBytes(StandardCharsets.UTF_8)).base64Encode(Base64Alphabet.MOD_CRYPT, false).drainToString()); } @Test public void testModCryptEncodeFoob() { assertEquals("NaxjMU", ByteIterator.ofBytes("foob".getBytes(StandardCharsets.UTF_8)).base64Encode(Base64Alphabet.MOD_CRYPT, false).drainToString()); } @Test public void testModCryptEncodeFooba() { assertEquals("NaxjMa2", ByteIterator.ofBytes("fooba".getBytes(StandardCharsets.UTF_8)).base64Encode(Base64Alphabet.MOD_CRYPT, false).drainToString()); } @Test public void testModCryptEncodeFoobar() { assertEquals("NaxjMa3m", ByteIterator.ofBytes("foobar".getBytes(StandardCharsets.UTF_8)).base64Encode(Base64Alphabet.MOD_CRYPT, false).drainToString()); } /* * ModCrypt Base64 alphabet decoding * (Expected values by https://github.com/magthe/sandi/blob/master/test-src/Codec/Binary/XxTest.hs) */ @Test public void testModCryptDecodeF() throws Exception { assertEquals("f", CodePointIterator.ofString("NU").base64Decode(Base64Alphabet.MOD_CRYPT, false).asUtf8String().drainToString()); } @Test public void testModCryptDecodeFo() throws Exception { assertEquals("fo", CodePointIterator.ofString("Naw").base64Decode(Base64Alphabet.MOD_CRYPT, false).asUtf8String().drainToString()); } @Test public void testModCryptDecodeFoo() throws Exception { assertEquals("foo", CodePointIterator.ofString("Naxj").base64Decode(Base64Alphabet.MOD_CRYPT, false).asUtf8String().drainToString()); } @Test public void testModCryptDecodeFoob() throws Exception { assertEquals("foob", CodePointIterator.ofString("NaxjMU").base64Decode(Base64Alphabet.MOD_CRYPT, false).asUtf8String().drainToString()); } @Test public void testModCryptDecodeFooba() throws Exception { assertEquals("fooba", CodePointIterator.ofString("NaxjMa2").base64Decode(Base64Alphabet.MOD_CRYPT, false).asUtf8String().drainToString()); } @Test public void testModCryptDecodeFoobar() throws Exception { assertEquals("foobar", CodePointIterator.ofString("NaxjMa3m").base64Decode(Base64Alphabet.MOD_CRYPT, false).asUtf8String().drainToString()); } /* * ModCrypt LE Base64 alphabet encoding * (Expected values by https://github.com/olethanh/django-phpbb/blob/master/phpbb/password_unittest.py) */ @Test public void testModCryptLeEncodeF() { assertEquals("a/", CodePointIterator.ofString("f").asLatin1().base64Encode(Base64Alphabet.MOD_CRYPT_LE, false).drainToString()); } @Test public void testModCryptLeEncodeFoobar() { assertEquals("axqPW3aQ", CodePointIterator.ofString("foobar").asLatin1().base64Encode(Base64Alphabet.MOD_CRYPT_LE, false).drainToString()); } /* * ModCrypt Base64 alphabet decoding * (Expected values by https://github.com/olethanh/django-phpbb/blob/master/phpbb/password_unittest.py) */ @Test public void testModCryptLeDecodeF() throws Exception { assertEquals("f", CodePointIterator.ofChars("a/".toCharArray()).base64Decode(Base64Alphabet.MOD_CRYPT_LE, false).asUtf8String().drainToString()); } @Test public void testModCryptLeDecodeFoobar() throws Exception { assertEquals("foobar", CodePointIterator.ofChars("axqPW3aQ".toCharArray()).base64Decode(Base64Alphabet.MOD_CRYPT_LE, false).asUtf8String().drainToString()); } /* * Decoding of invalid input */ @Test(expected=IllegalArgumentException.class) public void testInvalidInputDecodePadding1() throws Exception { CodePointIterator.ofString("=").base64Decode(Base64Alphabet.STANDARD, false).drain(); } @Test(expected=IllegalArgumentException.class) public void testInvalidInputDecodePadding2() throws Exception { CodePointIterator.ofString("==").base64Decode(Base64Alphabet.STANDARD, false).drain(); } @Test(expected=IllegalArgumentException.class) public void testInvalidInputDecodePadding3() throws Exception { CodePointIterator.ofString("===").base64Decode(Base64Alphabet.STANDARD, false).drain(); } @Test(expected=IllegalArgumentException.class) public void testInvalidInputDecodeNonAlphabeticChar() throws Exception { CodePointIterator.ofString("áááááááááááá").base64Decode(Base64Alphabet.STANDARD, false).drain(); } public void testInvalidInputDecodeTooMuchPadding() throws Exception { final CodePointIterator r = CodePointIterator.ofString("YWI=="); r.base64Decode(Base64Alphabet.STANDARD, false).drain(); assertTrue(r.hasNext()); assertEquals('=', r.next()); assertFalse(r.hasNext()); } /* * General Base64 tests */ /** * Tests if encoding/decoding works properly. * (data length) % 3 == 0 */ @Test public void testEncodeDecodeToByteStringBuilderMod0() throws Exception { doEncodeDecodeTest(generateSequence(255)); } /** * Tests if encoding/decoding works properly. * (data length) % 3 == 1 */ @Test public void testEncodeDecodeToByteStringBuilderMod1() throws Exception { doEncodeDecodeTest(generateSequence(256)); } /** * Tests if encoding/decoding works properly. * (data length) % 3 == 2 */ @Test public void testEncodeDecodeToByteStringBuilderMod2() throws Exception { doEncodeDecodeTest(generateSequence(257)); } private void doEncodeDecodeTest(byte[] inputData) throws Exception { byte[] outputData = ByteIterator.ofBytes(inputData).base64Encode().base64Decode().drain(); assertArrayEquals("Encode-Decode test failed, results are not the same.", inputData, outputData); } private byte[] generateSequence(final int len) { byte[] data = new byte[len]; for (int i = 0; i < len ; i++) { data[i] = (byte)i; } return data; } }