package ecologylab.serialization.deserializers.parsers.tlv;
/*
*
*
* Copyright 1990-2007 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.util.Calendar;
/**
* This class implements miscellaneous utility methods including
* those used for conversion of BigIntegers to
* byte arrays, hexadecimal printing of byte arrays etc.
*/
public class Utils {
/** UTF-8 encoding name. */
public static final String utf8 = "UTF-8";
/** Hexadecimal digits. */
private static char[] hc = { '0', '1', '2', '3', '4', '5', '6',
'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
/**
* Returns hex value for given sequence of bytes.
* @param b source data
* @param off offset of the first byte
* @param len length of the value
* @return hex value for given sequence of bytes.
*/
public static String hexNumber(byte[] b, int off, int len) {
char[] r;
int v;
int i;
int j;
if ((b == null) || (len == 0)) {
return "";
}
if ((off < 0) || (len < 0)) {
throw new ArrayIndexOutOfBoundsException();
}
r = new char[len * 2];
for (i = 0, j = 0;;) {
v = b[off + i] & 0xff;
r[j++] = hc[v >>> 4];
r[j++] = hc[v & 0x0f];
i++;
if (i >= len) {
break;
}
}
return (new String(r, 0, j));
}
/**
* Returns hex value for given byte.
* @param b source data
* @return hex value.
*/
public static String hexByte(int b) {
b = b & 0xff;
return new String(new char[] { hc[b >>> 4], hc[b & 0x0f] });
}
/**
* Checks if two byte arrays match.
* @param a the first byte array
* @param aOff starting offset for comparison within a
* @param aLen length of data in the first array
* @param b the second byte array
* @param bOff starting offset for comparison within b
* @param bLen length of data in the second array
* @return true if the sequence of len bytes in a starting at
* aOff matches those in b starting at bOff, false otherwise
*/
public static boolean byteMatch(byte[] a, int aOff, int aLen,
byte[] b, int bOff, int bLen) {
if ((aLen != bLen) || (a.length < aOff + aLen)
|| (b.length < bOff + bLen)) {
return false;
}
for (int i = 0; i < aLen; i++) {
if (a[i + aOff] != b[i + bOff])
return false;
}
return true;
}
/**
* Checks if two byte arrays match.
* @param a the first byte array
* @param b the second byte array
* @return true if both arrays has the same length and contents
*/
public static boolean byteMatch(byte[] a, byte[] b) {
return byteMatch(a, 0, a.length, b, 0, b.length);
}
/**
* Converts a sequence of bytes into a printable OID,
* a string of decimal digits, each separated by a ".".
* @param buffer byte array containing the bytes to be converted
* @param offset starting offset of the byte subsequence inside b
* @param length number of bytes to be converted
* @return printable OID
*/
public static String OIDtoString(byte[] buffer, int offset,
int length) {
StringBuffer result;
int end;
int t;
int x;
int y;
if (length == 0) {
return "";
}
result = new StringBuffer(40);
end = offset + length;
// first byte (t) always represents the first 2 values (x, y).
// t = (x * 40) + y;
t = buffer[offset++] & 0xff;
x = t / 40;
y = t - (x * 40);
result.append(x);
result.append('.');
result.append(y);
x = 0;
while (offset < end) {
// 7 bit per byte, bit 8 = 0 means the end of a value
x = x << 7;
t = buffer[offset++];
if (t >= 0) {
x += t;
result.append('.');
result.append(x);
x = 0;
} else {
x += t & 0x7f;
}
}
return result.toString();
}
/**
* Converst OID from string representation into byte array.
* @param oid string representation of OID
* @return byte array containing DER value for this OID.
*/
public static byte[] StringToOID(String oid) {
if (oid.indexOf('-') != -1) {
throw new IllegalArgumentException(oid);
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
int i = 0;
int b1 = 0;
int current = 0;
while (current < oid.length()) {
i++;
int k = oid.indexOf('.', current);
if (k == -1) {
k = oid.length();
}
int v = Integer.parseInt(oid.substring(current, k));
current = k + 1;
if (i == 1) {
b1 = v;
continue;
}
if (i == 2) {
v = b1 * 40 + v;
if (v > 255) {
throw new IllegalArgumentException(oid);
}
out.write(v);
continue;
}
int p = 0;
k = v;
while (true) {
p += 1;
k = k >> 7;
if (k == 0) {
break;
}
}
k = v;
while (p > 0) {
byte x = (byte) (k >> ((p - 1) * 7));
if (p == 1) {
x &= 0x7f;
} else {
x |= 0x80;
}
p--;
out.write(x);
}
}
if (i < 2) {
throw new IllegalArgumentException(oid);
}
return out.toByteArray();
}
/**
* Retrieves short value from byte array.
* @param data byte array
* @param offset value offset
* @return the short value
*/
public static short getShort(byte[] data, int offset) {
return (short) getU2(data, offset);
}
/**
* Retrieves unsigned 2-byte value from byte array.
* @param data byte array
* @param offset value offset
* @return the value
*/
public static int getU2(byte[] data, int offset) {
return ((data[offset] & 0xff) << 8) | (data[offset + 1] & 0xff);
}
/**
* Constructs integer value from byte array data.
* @param data the byte array.
* @param offset offset of the data.
* @return the integer value.
*/
public static int getInt(byte[] data, int offset) {
int l = 0;
for (int k = 0; k < 4; k++) {
l = (l << 8) | (data[offset++] & 0xFF);
}
return l;
}
/**
* Calculates SHA-1 hash for given data.
* @param inBuf array containing the data
* @param inOff data offset
* @param inLen data length
* @return SHA-1 hash
*/
public static byte[] getHash(byte[] inBuf, int inOff, int inLen) {
try {
MessageDigest sha = MessageDigest.getInstance("SHA");
sha.reset();
byte[] hash = new byte[20];
hash = sha.digest(inBuf);
return hash;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* Returns byte array which contains encoded short value.
* @param i the value
* @return byte array
*/
public static byte[] shortToBytes(int i) {
byte[] data = new byte[2];
data[0] = (byte) (i >> 8);
data[1] = (byte) i;
return data;
}
/**
* Returns byte array that contains sequence of encoded short values.
* @param data the short values
* @return byte array
*/
public static byte[] shortsToBytes(short[] data) {
byte[] d = new byte[2 * data.length];
for (int i = 0; i < data.length; i++) {
d[i * 2] = (byte) (data[i] >> 8);
d[i * 2 + 1] = (byte) data[i];
}
return d;
}
/**
* Returns UTF 8 encoding for this string.
* @param s the string
* @return UTF 8 encoding
*/
public static byte[] stringToBytes(String s) {
try {
return s.getBytes(utf8);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
System.out.println("Internal error: unsupported encoding");
return null;
}
/**
* Converts the calender to a string.
* @param calendar input date information
* @return formatted calendar string
*/
public static String calendarToString(Calendar calendar) {
String[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
String[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri",
"Sat" };
if (calendar == null) {
return "Thu Jan 01 00:00:00 UTC 1970";
}
int dow = calendar.get(Calendar.DAY_OF_WEEK);
int month = calendar.get(Calendar.MONTH);
int day = calendar.get(Calendar.DAY_OF_MONTH);
int hour_of_day = calendar.get(Calendar.HOUR_OF_DAY);
int minute = calendar.get(Calendar.MINUTE);
int seconds = calendar.get(Calendar.SECOND);
int year = calendar.get(Calendar.YEAR);
String yr = Integer.toString(year);
// TimeZone zone = calendar.getTimeZone();
// String zoneID = zone.getID();
// if (zoneID == null) zoneID = "";
String zoneID = "GMT";
// The total size of the string buffer
// 3+1+3+1+2+1+2+1+2+1+2+1+zoneID.length+1+yr.length
// = 21 + zoneID.length + yr.length
StringBuffer sb = new StringBuffer(25 + zoneID.length()
+ yr.length());
sb.append(days[dow - 1]).append(' ');
sb.append(months[month]).append(' ');
appendTwoDigits(sb, day).append(' ');
appendTwoDigits(sb, hour_of_day).append(':');
appendTwoDigits(sb, minute).append(':');
appendTwoDigits(sb, seconds).append(' ');
if (zoneID.length() > 0)
sb.append(zoneID).append(' ');
appendFourDigits(sb, year);
return sb.toString();
}
/**
* Appends zero filled numeric string for two digit numbers.
* @param sb current formatted buffer
* @param number the digit to format
* @return updated formatted string buffer
*/
private static final StringBuffer appendTwoDigits(StringBuffer sb,
int number) {
if (number < 10) {
sb.append('0');
}
return sb.append(number);
}
/**
* Appends zero filled numeric string for four digit numbers.
* @param sb current formatted buffer
* @param number the digit to format
* @return updated formatted string buffer
*/
private static final StringBuffer appendFourDigits(StringBuffer sb,
int number) {
if (number >= 0 && number < 1000) {
sb.append('0');
if (number < 100) {
sb.append('0');
}
if (number < 10) {
sb.append('0');
}
}
return sb.append(number);
}
/**
* Writes hex representation of byte array elements.
* @param writer where to write
* @param data data to be written
*/
public static void writeHex(PrintStream writer, byte[] data) {
String s = "";
for (int i = 0; i < data.length; i++) {
if (data[i] > -1 && data[i] < 16) {
s = s + "0";
}
s = s + Integer.toHexString(data[i] & 0xff) + " ";
if ((i + 1) % 16 == 0) {
writer.println(s);
s = "";
}
}
if (s.length() != 4) {
writer.println(s);
}
}
/**
* Converts string to array of shorts
* @param str input string
* @return output array of shorts
*/
public static short[] stringToShorts(String str) {
byte[] b = str.getBytes();
short[] res = new short[1 + (b.length / 4)];
for (int i = 0; i < b.length; i++) {
if ((b[i] >= 0x30) & (b[i] <= 0x39)) {
b[i] = (byte) (b[i] - 0x30);
} else {
if ((b[i] >= 0x41) & (b[i] <= 0x46)) {
b[i] = (byte) (b[i] - 0x41 + 0xa);
} else {
if ((b[i] >= 0x61) & (b[i] <= 0x66)) {
b[i] = (byte) (b[i] - 0x61 + 0xa);
}
}
}
}
res[0] = 0x3fff;
for (int i = 0; i < res.length - 1; i++) {
res[i + 1] = (short) ((b[4 * i] << 12)
| (b[4 * i + 1] << 8) | (b[4 * i + 2] << 4) | b[4 * i + 3]);
}
return res;
}
/**
* Creates java source for static array initialization.
* @param writer where to write
* @param name array name
* @param data initial values
*/
public static void writeDataArray(PrintStream writer, String name,
byte[] data) {
writer.println();
String s = " static byte[] " + name + " = { ";
for (int i = 0; i < data.length; i++) {
if (i != 0) {
s = s + ", ";
}
String h = "" + data[i];
if (s.length() + h.length() > 76) {
writer.println(s);
s = " ";
}
s = s + h;
}
writer.println(s + "};");
writer.println();
}
}