Converting hex string into byte array in javacard

894 views Asked by At

I have a byte array named 'hexa', which goes through some processes and array 'hexa1' is produced. packPDU returns a string array which has hexadecimal in the form of characters. I want the format of 'hexa1' to be following so that they can be interpreted as numbers:

byte[] hexa1 =  { (byte) 0x0F, (byte) 0xAA, (byte) 0x5C, (byte) 0x4E, (byte) 0x45 ,(byte) 0xA3, (byte) 0XA9, (byte) 0x68};

Following is the code I am using:

package com.bittest;



public class aditya1 {
public static void main(String[] args) {

    byte[] hexa = {'A','9', 'G', 'F', 'B', 'C', 'D', 'E', '5', '1'};
    byte[] gsm= convertUnicode2GSM(hexa);
    byte [] dst=new byte[gsm.length];
    byte[]  packed_array = packPDU(gsm, (short) 0, dst, (short) 0, (short) dst.length) ;

     byte[] hexa1 = new byte [(short)(packed_array.length/2)];
     for(short i =0; i<(short)((packed_array.length/2)-1); i++){
       hexa1[(short)(i+1)] = (byte)((short)(16*hexa[(short)(2*i+1)]) + (short)(hexa[(short)(2*i+2)]));
    }



}

public static byte[] convertUnicode2GSM(byte[] msg) {
       byte[] data = new byte[160];
       short j=0;
       for (short i = 0; i <(short) msg.length; i++) {
       switch (msg[i]) {
       case (byte)'@':  data[j] = 0x00; j++; break;
       case (byte)'$':  data[j] = 0x02; j++;break;
       case (byte)'\n': data[j] = 0x0A; j++;break;
       case (byte)'\r': data[j] = 0x0D; j++;break;
       case (byte)'_':  data[j] = 0x11; j++;break;
       //case (byte) 'ß':  data[j] = 0x1E; j++;break;
       case (byte)' ':  data[j] = 0x20; j++;break;
       case (byte)'!':  data[j] = 0x21; j++;break;

       case (byte) '\"': data[j] = 0x22; j++;break;
       case (byte)'#':  data[j] = 0x23; j++;break;
       case (byte)'%':  data[j] = 0x25; j++;break;
       case (byte)'&':  data[j] = 0x26; j++;break;
       case (byte)'\'': data[j] = 0x27; j++;break;
       case (byte)'(':  data[j] = 0x28; j++;break;
       case (byte)')':  data[j] = 0x29; j++;break;
       case (byte) '*':  data[j] = 0x2A; j++;break;
       case (byte)'+':  data[j] = 0x2B; j++;break;
       case (byte)',':  data[j] = 0x2C; j++;break;
       case (byte)'-':  data[j] = 0x2D; j++;break;
       case (byte) '.':  data[j] = 0x2E; j++;break;
       case (byte)'/':  data[j] = 0x2F; j++;break;
       case (byte)'0':  data[j] = 0x30; j++;break;
       case (byte)'1':  data[j] = 0x31; j++;break;
       case (byte)'2':  data[j] = 0x32; j++;break;
       case (byte)'3':  data[j] = 0x33; j++;break;
       case (byte)'4':  data[j] = 0x34; j++;break;
       case (byte)'5':  data[j] = 0x35; j++;break;
       case (byte)'6':  data[j] = 0x36; j++;break;
       case (byte)'7':  data[j] = 0x37; j++;break;
       case (byte)'8':  data[j] = 0x38; j++;break;
       case(byte) '9':  data[j] = 0x39; j++;break;
       case (byte)':':  data[j] = 0x3A; j++;break;
       case (byte)';':  data[j] = 0x3B; j++;break;
       case (byte)'<':  data[j] = 0x3C; j++;break;
       case (byte)'=':  data[j] = 0x3D; j++;break;
       case (byte)'>':  data[j] = 0x3E; j++;break;
       case (byte)'?':  data[j] = 0x3F; j++;break;
       case (byte)'A':  data[j] = 0x41; j++;break;
       case (byte)'B':  data[j] = 0x42; j++;break;
       case (byte)'C':  data[j] = 0x43; j++;break;
       case (byte)'D':  data[j] = 0x44; j++;break;
       case (byte)'E':  data[j] = 0x45; j++;break;
       case (byte)'F':  data[j] = 0x46; j++;break;
       case (byte)'G':  data[j] = 0x47; j++;break;
       case (byte)'H':  data[j] = 0x48; j++;break;
       case (byte)'I':  data[j] = 0x49; j++;break;
       case (byte)'J':  data[j] = 0x4A; j++;break;
       case (byte)'K':  data[j] = 0x4B; j++;break;
       case (byte)'L':  data[j] = 0x4C; j++;break;
       case (byte)'M':  data[j] = 0x4D; j++;break;
       case (byte)'N':  data[j] = 0x4E; j++;break;
       case (byte)'O':  data[j] = 0x4F; j++;break;
       case (byte)'P':  data[j] = 0x50; j++;break;
       case (byte)'Q':  data[j] = 0x51; j++;break;
       case (byte)'R':  data[j] = 0x52; j++;break;
       case (byte)'S':  data[j] = 0x53; j++;break;
       case (byte)'T':  data[j] = 0x54; j++;break;
       case (byte)'U':  data[j] = 0x55; j++;break;
       case (byte)'V':  data[j] = 0x56; j++;break;
       case (byte)'W':  data[j] = 0x57; j++;break;
       case (byte)'X':  data[j] = 0x58; j++;break;
       case (byte)'Y':  data[j] = 0x59; j++;break;
       case (byte)'Z':  data[j] = 0x5A; j++;break;
     //  case (byte) 'Ü':  data[j] = 0x5E; j++;break;
     //  case (byte) '§':  data[j] = 0x5F; j++;break;
       case (byte)'a':  data[j] = 0x61; j++;break;
       case (byte)'b':  data[j] = 0x62; j++;break;
       case (byte) 'c':  data[j] = 0x63; j++;break;
       case (byte)'d':  data[j] = 0x64; j++;break;
       case (byte)'e':  data[j] = 0x65; j++;break;
       case (byte)'f':  data[j] = 0x66; j++;break;
       case (byte)'g':  data[j] = 0x67; j++;break;
       case (byte)'h':  data[j] = 0x68; j++;break;
       case (byte)'i':  data[j] = 0x69; j++;break;
       case (byte)'j':  data[j] = 0x6A; j++;break;
       case (byte)'k':  data[j] = 0x6B; j++;break;
       case (byte)'l':  data[j] = 0x6C; j++;break;
       case (byte)'m':  data[j] = 0x6D; j++;break;
       case (byte)'n':  data[j] = 0x6E; j++;break;
       case (byte)'o':  data[j] = 0x6F; j++;break;
       case (byte)'p':  data[j] = 0x70; j++;break;
       case (byte)'q':  data[j] = 0x71; j++;break;
       case (byte)'r':  data[j] = 0x72; j++;break;
       case (byte)'s':  data[j] = 0x73; j++;break;
       case (byte)'t':  data[j] = 0x74; j++;break;
       case (byte)'u':  data[j] = 0x75; j++;break;
       case (byte)'v':  data[j] = 0x76; j++;break;
       case (byte)'w':  data[j] = 0x77; j++;break;
       case (byte)'x':  data[j] = 0x78; j++;break;
       case (byte)'y':  data[j] = 0x79; j++;break;
       case (byte)'z':  data[j] = 0x7A; j++;break;
       case (byte) '|':
                        data[j] = 0x1B; 
                        j +=1;
                        data[j] = 0x40;

                        j++;
                        break;


       default:   data[j] = 0x3F; j++;break; // '?'
       }  // switch
       }  // for
       return data;

       }  // convertUnicode2GSM

public static byte[] packPDU(byte[] src, short offsetSrc, byte[] dst, short offsetDst, short length) {

    short countSrc = (short) 0;
    short countDst = (short) 0;
    short countCurrent;
    byte leftover = (byte) 0;

    while (countSrc < length) {
        countCurrent = (byte) (countSrc & 7);
        if (countCurrent == 0) {
            leftover = src[(short) (offsetSrc)];
        } else {
            dst[offsetDst] = (byte) ((src[offsetSrc] << (8 - countCurrent)) | leftover);
            leftover = (byte) (src[offsetSrc] >> countCurrent);
            offsetDst++;
            countDst++;
        }
        countSrc++;
        offsetSrc++;
    }

    if ((length % 8) != 0) {
        dst[offsetDst] = leftover;
        countDst++;
    }

    return dst;
}

}

1

There are 1 answers

2
Maarten Bodewes On

I've put this through Java SE testing but I haven't uploaded it yet to Java Card. Paul Bastian seems to have done so though.

Comments on the hex conversion are within the code. Hopefully it also shows some best programming practices:

public final class HexCodec {

    private static final short NIBBLE_SIZE = 4;

    public static final short REASON_DATA_BUFFER_NOT_LARGE_ENOUGH = 0x0001;
    public static final short REASON_INVALID_ENCODING_SIZE = 0x0002;
    public static final short REASON_INVALID_ENCODING_CHARACTER = 0x0003;
    public static final short REASON_INVALID_DATA_SIZE = 0x0004;

    /**
     * Decodes the hexadecimal encoded bytes in the input buffer and puts them
     * in the output buffer.
     * Hex digits need to be ASCII encoded and the letters need to be uppercase.
     * Each byte needs to be encoded using exactly two hexadecimal digits.
     * WARNING: this function doesn't currently validate offset and length
     * arguments.
     * 
     * @param in
     *            the input buffer
     * @param inOff
     *            the offset in the input buffer containing the hexadecimal
     *            bytes
     * @param inLen
     *            the length in the input buffer of the hexadecimal bytes
     * @param out
     *            the output buffer
     * @param outOff
     *            the offset in the output buffer of the decoded bytes
     * @return the length in the output buffer of the decoded bytes
     * @throws CardRuntimeException
     *             with the following reason codes:
     *             <nl>
     *             <li>
     *             {@link HexCodec#REASON_INVALID_ENCODING_SIZE} : if the
     *             encoding size is not a multiple of 2</li>
     *             <li>
     *             {@link HexCodec#REASON_DATA_BUFFER_NOT_LARGE_ENOUGH} : if
     *             the output buffer cannot hold the decoded data</li>
     *             <li>
     *             {@link HexCodec#REASON_INVALID_ENCODING_CHARACTER} : if
     *             the encoding contains characters outside the uppercase
     *             hexadecimals</li>
     *             </nl>
     */
    private static short fromUppercaseHex(final byte[] in, final short inOff,
            final short inLen,
            final byte[] out, final short outOff) {

        // doesn't validate offsets in buffer

        // odd number of hex characters not allowed
        if (inLen % 2 != 0) {
            throw createCardRuntimeException(REASON_INVALID_ENCODING_SIZE);
        }

        final short outLen = (short) (inLen / 2);

        // make sure we have enough room in the buffer *before* decoding
        final short outEnd = (short) (outOff + outLen);
        if (outEnd < 0
                || outEnd > (short) out.length) {
            throw createCardRuntimeException(REASON_DATA_BUFFER_NOT_LARGE_ENOUGH);
        }

        // main decode loop
        for (short i = 0; i < outLen; i++) {
            byte b;

            // decodes high nibble of b
            final byte hexHi = in[(short) (inOff + i * 2)];
            if (hexHi >= '0' && hexHi <= '9') {
                b = (byte) ((hexHi - '0') << NIBBLE_SIZE);
            } else if (hexHi >= 'A' && hexHi <= 'F') {
                b = (byte) ((hexHi - 'A' + 10) << NIBBLE_SIZE);
            } else {
                throw createCardRuntimeException(REASON_INVALID_ENCODING_CHARACTER);
            }

            // decodes low nibble of b
            final byte hexLo = in[(short) (inOff + i * 2 + 1)];
            if (hexLo >= '0' && hexLo <= '9') {
                b |= (byte) (hexLo - '0');
            } else if (hexLo >= 'A' && hexLo <= 'F') {
                b |= (byte) (hexLo - 'A' + 10);
            } else {
                throw createCardRuntimeException(REASON_INVALID_ENCODING_CHARACTER);
            }

            out[(short) (outOff + i)] = b;
        }

        return outLen;
    }

    /**
     * Encodes the bytes in the input buffer and puts the hexadecimals in the
     * output buffer.
     * The hex digits will be ASCII encoded and the letters will be in
     * uppercase.
     * Each byte will be encoded using exactly two hexadecimal digits.
     * WARNING: this function doesn't currently validate offset and length
     * arguments.
     * 
     * @param in
     *            the input buffer
     * @param inOff
     *            the offset in the input buffer containing the binary data
     *            bytes
     * @param inLen
     *            the length in the input buffer of the binary data
     * @param out
     *            the output buffer
     * @param outOff
     *            the offset in the output buffer for the hexadecimal digits
     * @return the number of hexadecimal digits
     * @throws CardRuntimeException
     *             with the following reason codes:
     *             <nl>
     *             <li>
     *             {@link HexCodec#REASON_INVALID_DATA_SIZE} : if the output
     *             buffer cannot hold the encoded data</li>
     *             </nl>
     */
    private static short toUppercaseHex(
            final byte[] in, final short inOff, final short inLen,
            final byte[] out, final short outOff) {

        // doesn't validate offsets in buffer

        final short outLen = (short) (inLen * 2);
        final short outEnd = (short) (outOff + outLen);

        // make sure we have enough room in the buffer *before* decoding
        if (outEnd < 0 || outEnd > (short) out.length) {
            throw createCardRuntimeException(REASON_INVALID_DATA_SIZE);
        }

        // main encode loop
        for (short i = 0; i < inLen; i++) {
            final byte b = in[(short) (inOff + i)];

            // encodes high nibble of b
            final byte bHi = (byte) ((b >> NIBBLE_SIZE) & 0x0F);
            if (bHi < 10) {
                out[(short) (outOff + i * 2)] = (byte) ('0' + bHi);
            } else {
                out[(short) (outOff + i * 2)] = (byte) ('A' + bHi - 10);
            }

            // encodes low nibble of b
            final byte bLo = (byte) (b & 0x0F);
            if (bLo < 10) {
                out[(short) (outOff
                        + i * 2 + 1)] = (byte) ('0' + bLo);
            } else {
                out[(short) (outOff
                        + i * 2 + 1)] = (byte) ('A' + bLo - 10);
            }
        }

        return outLen;
    }

    /**
     * Creates a CardRuntimeException with the given reason code and returns it
     * so it can be thrown.
     * This alleviates the issue of the Java compiler not recognizing `throwIt`
     * as exit point.
     * WARNING: do not forget to actually throw the exception returned.
     * 
     * @param reason
     *            the reason code of the exception
     * @return the exception generated by the runtime environment (through
     *         <code>CardRuntimeException.throwIt</code>)
     */
    private static CardRuntimeException createCardRuntimeException(
            final short reason) {
        try {
            CardRuntimeException.throwIt(reason);
        } catch (CardRuntimeException e) {
            return e;
        }
        // should never be reached (but the compiler doesn't know that)
        return null;
    }

    private HexCodec() {
        // avoid instantiation
    }
}