/*
* passportapplet - A reference implementation of the MRTD standards.
*
* Copyright (C) 2006 SoS group, Radboud University
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* $Id: PassportUtil.java 539 2008-09-30 11:10:41Z martijno $
*/
package sos.passportapplet;
import javacard.framework.ISO7816;
/**
* Generic helpers for the Passport.
*
* Contains some commented debug code.
*
* @author Cees-Bart Breunese (ceesb@cs.ru.nl)
*
*/
public class PassportUtil implements ISO7816 {
/**
* Counts the number of set bits in a byte
*
* @param b byte to be counted
* @return 0 when number of bits in b is even
*/
public static byte evenBits(byte b) {
short count = 0;
for (short i = 0; i < 8; i++) {
count += (b >>> i) & 0x1;
}
return (byte) (count % 2);
}
/**
* Calculates the xor of byte arrays in1 and in2 into out.
*
* Arrays may be the same, but regions may not overlap.
*
*
* @param in1 input array
* @param in1_o offset of input array
* @param in2 input array
* @param in2_o offset of inputarray
* @param out output array
* @param out_o offset of output array
* @param len length of xor
*/
public static void xor(byte[] in1, short in1_o, byte[] in2,
short in2_o, byte[] out, short out_o, short len) {
for(short s=0; s < len; s++) {
out[(short)(out_o + s)] = (byte)(in1[(short)(in1_o + s)] ^ in2[(short)(in2_o + s)]);
}
}
/**
* Swaps two non-overlapping segments of the same length in the same byte array
* in place.
*
* @param buffer a byte array
* @param offset1 offset to first byte array
* @param offset2 offset to the second byte array
* @param len length of the segments
*/
public static void swap(byte[] buffer, short offset1, short offset2, short len) {
byte byte1, byte2;
for(short i=0; i<len; i++) {
byte1 = buffer[(short)(offset1 + i)];
byte2 = buffer[(short)(offset2 + i)];
buffer[(short)(offset1 + i)] = byte2;
buffer[(short)(offset2 + i)] = byte1;
}
}
/**
* Returns the sign bit of a short as a short.
* @param a a short value
* @return the sign bit of a as a short
*/
public static short sign(short a) {
return (byte)((a >>> (short)15) & 1);
}
/**
* Returns the smallest unsigned short argument.
*
* @param a a short
* @param b another short
* @return smallest unsigned value a or b.
*/
public static short min(short a, short b) {
if(sign(a) == sign(b))
return (a < b ? a : b);
else if(sign(a) == 1)
return b;
else
return a;
}
// public static void throwShort(short s) {
// ISOException.throwIt((short)(0x6d00 | (s & 0xff)));
// }
//
// public static void returnDESKey(DESKey k, APDU apdu) {
// byte[] b = new byte[(short)(k.getSize()/8)];
//
// k.getKey(b, (short) 0);
// returnBuffer(b, apdu);
// }
/***
* Pads an input buffer with max 8 and min 1 byte padding (0x80 followed by optional zeros)
* relative to the offset and length given. Always pad with at least a 0x80 byte.
*
* See 6.2.3.1 in ISO7816-4
*
* @param buffer array to pad
* @param offset to data
* @param length of data
* @return new length, with padding, of data
*
*/
public static short pad(byte[] buffer, short offset, short len) {
short padbytes = (short)(lengthWithPadding(len) - len);
for(short i=0; i<padbytes; i++) {
buffer[(short)(offset+len+i)] = (i == 0 ? (byte)0x80 : 0x00);
}
return (short)(len + padbytes);
}
public static short lengthWithPadding(short inputLength) {
return (short)((((short)(inputLength + 8)) / 8) * 8);
}
// public static void pad(APDU aapdu, short pad_len) {
// byte[] apdu = aapdu.getBuffer();
// short apdu_len=0;
// short lc = (short)(apdu[ISO7816.OFFSET_LC] & 0xff);
// try {
// apdu_len = (short)(ISO7816.OFFSET_CDATA + lc);
// } catch(NullPointerException e) {
// ISOException.throwIt((short)0x6d66);
// }
//
// if(apdu_len < apdu.length)
// if(pad_len < CREFPassportCrypto.PAD_DATA.length)
// Util.arrayCopy(CREFPassportCrypto.PAD_DATA, (short)0, apdu,
// apdu_len,
// pad_len);
// }
// public static void returnBuffer(byte[] b, APDU apdu) {
// returnBuffer(b, (short)b.length, apdu);
// }
//
// public static void returnBuffer(byte[] b, short length, APDU apdu) {
// returnBuffer(b, (short)0, length, apdu);
// }
// public static void returnBuffer(byte[] b, short offset, short length, APDU aapdu) {
// byte[] apdu = aapdu.getBuffer();
// short le = aapdu.setOutgoing(); //(short)(apdu[(short)(apdu.length-2)] & 0xff);
//
// if (le >= b.length) {
// aapdu.setOutgoingLength(le);
// if(le > b.length) {
// pad(aapdu, (short)(le - (short)b.length));
// }
// }
// else
// {
// ISOException.throwIt(SW_WRONG_LENGTH);
// }
//
// aapdu.setOutgoingLength(length);
// Util.arrayCopy(b, offset, apdu, (short)0, (short) length);
// aapdu.sendBytes((short) 0,length);
//
// }
// public static void returnByte(byte b, APDU apdu) {
// byte[] buffer = apdu.getBuffer();
// short le = apdu.setOutgoing();
// if (le != 1) {
// ISOException.throwIt(SW_WRONG_LENGTH);
// }
//
// apdu.setOutgoingLength((short) 1);
// buffer[0] = b;
// apdu.sendBytes((short) 0, (short) 1);
// }
/***
* Computes the actual length of a data block as byte value, without the padding.
*
* @param apdu containing data
* @param offset to data
* @param length of data
* @return new length of data, without padding
*/
public static byte calcLcFromPaddedData(byte[] apdu, short offset, short length) {
for(short i=(short)(length - 1) ; i>=0; i--)
if(apdu[(short)(offset + i)] != 0)
if((apdu[(short)(offset + i)] & 0xff)!= 0x80)
// not padded
return (byte)(length & 0xff);
else
return (byte)(i & 0xff);
return 0;
}
}