Lucia-App/app/src/main/java/digital/selfdefense/lucia/Z85.java

206 lines
7.2 KiB
Java

package digital.selfdefense.lucia;
/*
* Copyright (c) 2015, Bubelich Mykola (bubelich.com)
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* Neither the name of the copyright holder nor the names of its contributors
* may be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
import java.nio.ByteBuffer;
import java.util.Arrays;
public final class Z85 {
private final static char[] _ALPHA = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.-:+=^!/*?&<>()[]{}@%$#".toCharArray();
private final static int[] _RALPHA = {
68, 0, 84, 83, 82, 72, 0, 75, 76, 70, 65, 0, 63, 62,
69, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 64, 0, 73, 66, 74, 71,
81, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61,
77, 0, 78, 67, 0, 0, 10, 11, 12, 13, 14, 15, 16, 17, 18,
19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32,
33, 34, 35, 79, 0, 80};
private final static int _RALSHIFT = 33;
public static String encode(byte[] input) throws RuntimeException {
if (input == null || input.length == 0) {
throw new IllegalArgumentException("Input is wrong");
}
int length = input.length;
int index = 0;
byte[] buff = new byte[4];
StringBuilder sb = new StringBuilder((input.length * 5 / 4) + 1);
while (length >= 4) {
buff[3] = input[index++];
buff[2] = input[index++];
buff[1] = input[index++];
buff[0] = input[index++];
sb.append(encodeQuarter(buff));
length -= 4;
}
if (length > 0) {
buff = new byte[length];
for (int i = length - 1; i >= 0; i--) {
buff[i] = input[index++];
}
sb.append(encodePadding(buff));
}
return sb.toString();
}
public static byte[] decode(String input) throws RuntimeException {
if (input == null || input.length() == 0) {
throw new IllegalArgumentException("Input is wrong");
}
int length = input.length();
int index = 0;
char[] buff = new char[5];
ByteBuffer bytebuff = ByteBuffer.allocate((length * 4 / 5));
while (length >= 5) {
buff[0] = input.charAt(index++);
buff[1] = input.charAt(index++);
buff[2] = input.charAt(index++);
buff[3] = input.charAt(index++);
buff[4] = input.charAt(index++);
bytebuff.put(decodeQuarter(buff));
length -= 5;
}
if (length > 0) {
char[] padding = new char[length];
for (int i = 0; i < length; i++) {
padding[i] = input.charAt(index++);
}
bytebuff.put(decodePadding(padding));
}
bytebuff.flip();
if (bytebuff.limit() == 0) {
throw new RuntimeException("Output is empty");
}
return Arrays.copyOf(bytebuff.array(), bytebuff.limit());
}
private static char[] encodeQuarter(byte[] data) {
long value = (data[0] & 0x00000000000000FFL) |
((data[1] & 0x00000000000000FFL) << 8) |
((data[2] & 0x00000000000000FFL) << 16) |
((data[3] & 0x00000000000000FFL) << 24);
char[] out = new char[5];
out[0] = _ALPHA[(int) ((value / 0x31C84B1L) % 85)];
out[1] = _ALPHA[(int) ((value / 0x95EEDL) % 85)];
out[2] = _ALPHA[(int) ((value / 0x1C39L) % 85)];
out[3] = _ALPHA[(int) ((value / 0x55L) % 85)];
out[4] = _ALPHA[(int) ((value) % 85)];
return out;
}
/**
* Encode padding scheme
*
* @param data byte[] Array of length = 4 of data
* @return char[] Encoded padding
*/
private static char[] encodePadding(byte[] data) {
long value = 0;
int length = (data.length * 5 / 4) + 1;
char[] out = new char[length];
switch (data.length) {
case 3:
value |= (data[2] & 0x00000000000000FFL) << 16;
case 2:
value |= (data[1] & 0x00000000000000FFL) << 8;
}
value |= (data[0] & 0x00000000000000FFL);
switch (data.length) {
case 3:
out[3] = _ALPHA[(int) ((value / 0x95EEDL) % 85)];
case 2:
out[2] = _ALPHA[(int) ((value / 0x1C39L) % 85)];
}
out[1] = _ALPHA[(int) ((value / 0x55L) % 85)];
out[0] = _ALPHA[(int) ((value) % 85)];
return out;
}
private static byte[] decodeQuarter(char[] data) {
long value = 0;
value += _RALPHA[data[0] - _RALSHIFT] * 0x31C84B1L;
value += _RALPHA[data[1] - _RALSHIFT] * 0x95EEDL;
value += _RALPHA[data[2] - _RALSHIFT] * 0x1C39L;
value += _RALPHA[data[3] - _RALSHIFT] * 0x55L;
value += _RALPHA[data[4] - _RALSHIFT];
return new byte[]{
(byte) (value >>> 24),
(byte) (value >>> 16),
(byte) (value >>> 8),
(byte) (value)
};
}
private static byte[] decodePadding(char[] data) {
long value = 0;
int length = data.length * 4 / 5;
switch (data.length) {
case 4:
value += _RALPHA[data[3] - _RALSHIFT] * 0x95EEDL;
case 3:
value += _RALPHA[data[2] - _RALSHIFT] * 0x1C39L;
case 2:
value += _RALPHA[data[1] - _RALSHIFT] * 0x55L;
}
value += _RALPHA[data[0] - _RALSHIFT];
byte[] buff = new byte[length];
for (int i = length - 1; i >= 0; i--) {
buff[length - i - 1] = (byte) (value >>> 8 * i);
}
return buff;
}
}