aboutsummaryrefslogtreecommitdiffstats
path: root/test/lib/templates
Commit message (Expand)AuthorAgeFilesLines
* git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@9182 35acf78f-673a-0410...Giovanni Di Sirio2016-03-294-221/+0
* Mass license update.Giovanni Di Sirio2016-03-184-4/+4
* git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@7622 35acf78f-673a-0410...gdisirio2015-01-114-4/+4
* git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6698 35...gdisirio2014-02-101-13/+13
* git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6696 35...gdisirio2014-02-101-1/+1
* git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6694 35...gdisirio2014-02-101-1/+1
* git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6691 35...gdisirio2014-02-102-1/+10
* git-svn-id: svn://svn.code.sf.net/p/chibios/svn/branches/kernel_3_dev@6688 35...gdisirio2014-02-104-0/+212
8'>28 29 30 31 32 33 34 35 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 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195
/* Copyright (C) 2014 Vincent Breitmoser <v.breitmoser@mugenguild.com>
 *
 * 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.sufficientlysecure.keychain.util;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;

/** TLV data structure and parser implementation.
 *
 * This class is used for parsing and working with TLV packets as specified in
 * the Functional Specification of the OpenPGP application on ISO Smart Card
 * Operating Systems, and ISO 7816-4.
 *
 * Objects of this class are immutable data structs parsed from BER-TLV data.
 * They include at least the T, L and V fields they were originally parsed
 * from, subclasses may also carry more specific information.
 *
 * @see { @linktourl http://www.cardwerk.com/smartcards/smartcard_standard_ISO7816-4_annex-d.aspx}
 *
 */
public class Iso7816TLV {

    public final int mT;
    public final int mL;
    public final byte[] mV;

    private Iso7816TLV(int T, int L, byte[] V) {
        mT = T;
        mL = L;
        mV = V;
    }

    public String prettyPrint() {
        return prettyPrint(0);
    }

    public String prettyPrint(int indent) {
        // lol
        String padding = "                                                  ".substring(0, indent*2);
        return padding + String.format("tag T %4x L %04d", mT, mL);
    }

    /** Read a single Iso7816 TLV packet from a given ByteBuffer, either
     * recursively or flat.
     *
     * This is a convenience wrapper for readSingle with ByteBuffer argument.
     */
    public static Iso7816TLV readSingle(byte[] data, boolean recursive) throws IOException {
        return readSingle(ByteBuffer.wrap(data), recursive);
    }

    /** Read a single Iso7816 TLV packet from a given ByteBuffer, either
     * recursively or flat.
     *
     * If the recursive flag is true, a composite packet will be parsed and
     * returned as an Iso7816CompositeTLV. Otherwise, a regular Iso7816TLV
     * object will be returned regardless of actual packet type.
     *
     * This method is fail-fast, if any parsing error occurs it will throw an
     * exception.
     *
     */
    public static Iso7816TLV readSingle(ByteBuffer data, boolean recursive) throws IOException {

        int T = data.get() & 0xff;
        boolean composite = (T & 0x20) == 0x20;
        if ((T & 0x1f) == 0x1f) {
            int T2 = data.get() & 0xff;
            if ((T2 & 0x1f) == 0x1f) {
                throw new IOException("Only tags up to two bytes are supported!");
            }
            T = (T << 8) | (T2 & 0x7f);
        }

        // Log.d(Constants.TAG, String.format("T %02x", T));

        // parse length, according to ISO 7816-4 (openpgp card 2.0 specs, page 24)
        int L = data.get() & 0xff;
        if (L == 0x81) {
            L = data.get() & 0xff;
        } else if (L == 0x82) {
            L = data.get() & 0xff;
            L = (L << 8) | (data.get() & 0xff);
        } else if (L >= 0x80) {
            throw new IOException("Invalid length field!");
        }

        // Log.d(Constants.TAG, String.format("L %02x", L));

        // read L bytes into new buffer
        byte[] V = new byte[L];
        data.get(V);

        // if we are supposed to parse composites, do that
        if (recursive && composite) {
            // Log.d(Constants.TAG, "parsing composite TLV");
            Iso7816TLV[] subs = readList(V, true);
            return new Iso7816CompositeTLV(T, L, V, subs);
        }

        return new Iso7816TLV(T, L, V);

    }

    /** Parse a list of TLV packets from byte data, recursively or flat.
     *
     * This method is fail-fast, if any parsing error occurs it will throw an
     * exception.
     *
     */
    public static Iso7816TLV[] readList(byte[] data, boolean recursive) throws IOException {
        ByteBuffer buf = ByteBuffer.wrap(data);

        ArrayList<Iso7816TLV> result = new ArrayList<>();

        // read while data is available. this will fail if there is trailing data!
        while (buf.hasRemaining()) {
            // skip 0x00 and 0xFF filler bytes
            buf.mark();
            byte peek = buf.get();
            if (peek == 0xff || peek == 0x00) {
                continue;
            }
            buf.reset();

            Iso7816TLV packet = readSingle(buf, recursive);
            result.add(packet);
        }

        Iso7816TLV[] resultX = new Iso7816TLV[result.size()];
        result.toArray(resultX);
        return resultX;
    }

    /** This class represents a composite TLV packet.
     *
     * Note that only actual composite TLV packets are instances of this class.
     * A regular non-composite Iso7816TLV object may however be a composite
     * packet if it was parsed non-recursively.
     *
     */
    public static class Iso7816CompositeTLV extends Iso7816TLV {

        public final Iso7816TLV[] mSubs;

        public Iso7816CompositeTLV(int T, int L, byte[] V, Iso7816TLV[] subs) {
            super(T, L, V);

            mSubs = subs;
        }

        public String prettyPrint(int indent) {
            StringBuilder result = new StringBuilder();
            // lol
            result.append("                                                  ".substring(0, indent*2));
            result.append(String.format("composite tag T %4x L %04d", mT, mL));
            for (Iso7816TLV sub : mSubs) {
                result.append('\n');
                result.append(sub.prettyPrint(indent+1));
            }
            return result.toString();
        }

    }

    /** Recursively searches for a specific tag in a composite TLV packet structure, depth first. */
    public static Iso7816TLV findRecursive(Iso7816TLV in, int tag) {
        if (in.mT == tag) {
            return in;
        } else if (in instanceof Iso7816CompositeTLV) {
            for (Iso7816TLV sub : ((Iso7816CompositeTLV) in).mSubs) {
                Iso7816TLV result = findRecursive(sub, tag);
                if (result != null) {
                    return result;
                }
            }
        }
        return null;
    }

}