diff options
| author | Vincent Breitmoser <valodim@mugenguild.com> | 2014-09-29 20:06:42 +0200 | 
|---|---|---|
| committer | Vincent Breitmoser <valodim@mugenguild.com> | 2014-09-29 21:22:48 +0200 | 
| commit | c13a7b7eaecb849ede181fb5f72a23b9f5975562 (patch) | |
| tree | 88e02730c64810c0e4bc55adccf8f4346591d881 /OpenKeychain/src | |
| parent | c34a159cae783178b692022a584e1f937cc652ca (diff) | |
| download | open-keychain-c13a7b7eaecb849ede181fb5f72a23b9f5975562.tar.gz open-keychain-c13a7b7eaecb849ede181fb5f72a23b9f5975562.tar.bz2 open-keychain-c13a7b7eaecb849ede181fb5f72a23b9f5975562.zip | |
add class for proper nfc tlv packet parsing
Diffstat (limited to 'OpenKeychain/src')
| -rw-r--r-- | OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java | 195 | 
1 files changed, 195 insertions, 0 deletions
| diff --git a/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java new file mode 100644 index 000000000..90afd3bc0 --- /dev/null +++ b/OpenKeychain/src/main/java/org/sufficientlysecure/keychain/util/Iso7816TLV.java @@ -0,0 +1,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<Iso7816TLV>(); + +        // 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; +    } + +} | 
