1f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun/*
2f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * Copyright (C) 2016 The Android Open Source Project
3f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun *
4f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * Licensed under the Apache License, Version 2.0 (the "License");
5f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * you may not use this file except in compliance with the License.
6f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * You may obtain a copy of the License at
7f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun *
8f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun *      http://www.apache.org/licenses/LICENSE-2.0
9f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun *
10f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * Unless required by applicable law or agreed to in writing, software
11f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * distributed under the License is distributed on an "AS IS" BASIS,
12f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * See the License for the specific language governing permissions and
14f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * limitations under the License.
15f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun */
16f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
17f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunpackage com.android.internal.telephony.uicc.asn1;
18f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
19f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunimport android.annotation.Nullable;
20f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
21f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunimport com.android.internal.telephony.uicc.IccUtils;
22f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
23f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunimport java.nio.charset.StandardCharsets;
24f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunimport java.util.ArrayList;
25f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunimport java.util.Collections;
26f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunimport java.util.List;
27f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
28f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun/**
29f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * This represents a primitive or constructed data defined by ASN.1. A constructed node can have
30f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * child nodes. A non-constructed node can have a value. This class is read-only. To build a node,
31f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * you can use the {@link #newBuilder(int)} method to get a {@link Builder} instance. This class is
32f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun * not thread-safe.
33f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun */
34f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sunpublic final class Asn1Node {
35f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private static final int INT_BYTES = Integer.SIZE / Byte.SIZE;
36f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private static final List<Asn1Node> EMPTY_NODE_LIST = Collections.emptyList();
37f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
38f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // Bytes for boolean values.
39f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private static final byte[] TRUE_BYTES = new byte[] {-1};
40f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private static final byte[] FALSE_BYTES = new byte[] {0};
41f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
42f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
43f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * This class is used to build an Asn1Node instance of a constructed tag. This class is not
44f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * thread-safe.
45f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
46f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public static final class Builder {
47f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        private final int mTag;
48f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        private final List<Asn1Node> mChildren;
49f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
50f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        private Builder(int tag) {
51f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (!isConstructedTag(tag)) {
52f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                throw new IllegalArgumentException(
53f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                        "Builder should be created for a constructed tag: " + tag);
54f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
55f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            mTag = tag;
56f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            mChildren = new ArrayList<>();
57f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
58f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
59f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
60f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child from an existing node.
61f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
62f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
63f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws IllegalArgumentException If the child is a non-existing node.
64f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
65f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChild(Asn1Node child) {
66f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            mChildren.add(child);
67f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
68f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
69f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
70f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
71f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child from another builder. The child will be built with the call to this method,
72f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * and any changes to the child builder after the call to this method doesn't have effect.
73f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
74f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
75f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
76f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChild(Builder child) {
77f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            mChildren.add(child.build());
78f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
79f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
80f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
81f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
82f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds children from bytes. This method calls {@link Asn1Decoder} to verify the {@code
83f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * encodedBytes} and adds all nodes parsed from it as children.
84f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
85f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
86f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
87f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
88f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChildren(byte[] encodedBytes) throws InvalidAsn1DataException {
89f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            Asn1Decoder subDecoder = new Asn1Decoder(encodedBytes, 0, encodedBytes.length);
90f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            while (subDecoder.hasNextNode()) {
91f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                mChildren.add(subDecoder.nextNode());
92f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
93f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
94f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
95f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
96f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
97f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child of non-constructed tag with an integer as the data.
98f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
99f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
100f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws IllegalStateException If the {@code tag} is not constructed..
101f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
102f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChildAsInteger(int tag, int value) {
103f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (isConstructedTag(tag)) {
104f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
105f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
106f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            byte[] dataBytes = IccUtils.signedIntToBytes(value);
107f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            addChild(new Asn1Node(tag, dataBytes, 0, dataBytes.length));
108f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
109f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
110f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
111f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
112f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child of non-constructed tag with a string as the data.
113f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
114f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
115f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws IllegalStateException If the {@code tag} is not constructed..
116f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
117f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChildAsString(int tag, String value) {
118f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (isConstructedTag(tag)) {
119f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
120f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
121f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            byte[] dataBytes = value.getBytes(StandardCharsets.UTF_8);
122f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            addChild(new Asn1Node(tag, dataBytes, 0, dataBytes.length));
123f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
124f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
125f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
126f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
127f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child of non-constructed tag with a byte array as the data.
128f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
129f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @param value The value will be owned by this node.
130f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
131f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws IllegalStateException If the {@code tag} is not constructed..
132f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
133f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChildAsBytes(int tag, byte[] value) {
134f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (isConstructedTag(tag)) {
135f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
136f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
137f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            addChild(new Asn1Node(tag, value, 0, value.length));
138f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
139f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
140f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
141f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
142f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child of non-constructed tag with a byte array as the data from a hex string.
143f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
144f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
145f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws IllegalStateException If the {@code tag} is not constructed..
146f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
147f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChildAsBytesFromHex(int tag, String hex) {
148f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return addChildAsBytes(tag, IccUtils.hexStringToBytes(hex));
149f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
150f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
151f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
152f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child of non-constructed tag with bits as the data.
153f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
154f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
155f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws IllegalStateException If the {@code tag} is not constructed..
156f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
157f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChildAsBits(int tag, int value) {
158f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (isConstructedTag(tag)) {
159f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
160f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
161f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            // Always allocate 5 bytes for simplicity.
162f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            byte[] dataBytes = new byte[INT_BYTES + 1];
163f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            // Puts the integer into the byte[1-4].
164f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            value = Integer.reverse(value);
165f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            int dataLength = 0;
166f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            for (int i = 1; i < dataBytes.length; i++) {
167f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                dataBytes[i] = (byte) (value >> ((INT_BYTES - i) * Byte.SIZE));
168f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                if (dataBytes[i] != 0) {
169f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    dataLength = i;
170f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                }
171f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
172f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            dataLength++;
173f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            // The first byte is the number of trailing zeros of the last byte.
174f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            dataBytes[0] = IccUtils.countTrailingZeros(dataBytes[dataLength - 1]);
175f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            addChild(new Asn1Node(tag, dataBytes, 0, dataLength));
176f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
177f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
178f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
179f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /**
180f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * Adds a child of non-constructed tag with a boolean as the data.
181f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         *
182f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @return This builder.
183f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         * @throws IllegalStateException If the {@code tag} is not constructed..
184f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun         */
185f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Builder addChildAsBoolean(int tag, boolean value) {
186f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (isConstructedTag(tag)) {
187f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                throw new IllegalStateException("Cannot set value of a constructed tag: " + tag);
188f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
189f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            addChild(new Asn1Node(tag, value ? TRUE_BYTES : FALSE_BYTES, 0, 1));
190f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return this;
191f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
192f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
193f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        /** Builds the node. */
194f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        public Asn1Node build() {
195f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return new Asn1Node(mTag, mChildren);
196f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
197f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
198f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
199f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private final int mTag;
200f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private final boolean mConstructed;
201f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // Do not use this field directly in the methods other than the constructor and encoding
202f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // methods (e.g., toBytes()), but always use getChildren() instead.
203f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private final List<Asn1Node> mChildren;
204f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
205f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // Byte array that actually holds the data. For a non-constructed node, this stores its actual
206f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // value. If the value is not set, this is null. For constructed node, this stores encoded data
207f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // of its children, which will be decoded on the first call to getChildren().
208f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private @Nullable byte[] mDataBytes;
209f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // Offset of the data in above byte array.
210f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private int mDataOffset;
211f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // Length of the data in above byte array. If it's a constructed node, this is always the total
212f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // length of all its children.
213f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private int mDataLength;
214f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    // Length of the total bytes required to encode this node.
215f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private int mEncodedLength;
216f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
217f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
218f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Creates a new ASN.1 data node builder with the given tag. The tag is an encoded tag including
219f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * the tag class, tag number, and constructed mask.
220f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
221f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public static Builder newBuilder(int tag) {
222f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return new Builder(tag);
223f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
224f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
225f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private static boolean isConstructedTag(int tag) {
226f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        // Constructed mask is at the 6th bit.
227f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        byte[] tagBytes = IccUtils.unsignedIntToBytes(tag);
228f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return (tagBytes[0] & 0x20) != 0;
229f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
230f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
231f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private static int calculateEncodedBytesNumForLength(int length) {
232f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        // Constructed mask is at the 6th bit.
233f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        int len = 1;
234f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (length > 127) {
235f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            len += IccUtils.byteNumForUnsignedInt(length);
236f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
237f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return len;
238f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
239f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
240f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
241f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Creates a node with given data bytes. If it is a constructed node, its children will be
242f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * parsed when they are visited.
243f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
244f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    Asn1Node(int tag, @Nullable byte[] src, int offset, int length) {
245f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mTag = tag;
246f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        // Constructed mask is at the 6th bit.
247f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mConstructed = isConstructedTag(tag);
248f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mDataBytes = src;
249f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mDataOffset = offset;
250f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mDataLength = length;
251f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mChildren = mConstructed ? new ArrayList<Asn1Node>() : EMPTY_NODE_LIST;
252f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mEncodedLength =
253f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                IccUtils.byteNumForUnsignedInt(mTag)
254f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                        + calculateEncodedBytesNumForLength(mDataLength)
255f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                        + mDataLength;
256f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
257f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
258f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** Creates a constructed node with given children. */
259f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private Asn1Node(int tag, List<Asn1Node> children) {
260f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mTag = tag;
261f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mConstructed = true;
262f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mChildren = children;
263f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
264f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mDataLength = 0;
265f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        int size = children.size();
266f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        for (int i = 0; i < size; i++) {
267f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            mDataLength += children.get(i).mEncodedLength;
268f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
269f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        mEncodedLength =
270f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                IccUtils.byteNumForUnsignedInt(mTag)
271f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                        + calculateEncodedBytesNumForLength(mDataLength)
272f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                        + mDataLength;
273f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
274f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
275f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public int getTag() {
276f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return mTag;
277f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
278f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
279f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public boolean isConstructed() {
280f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return mConstructed;
281f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
282f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
283f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
284f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Tests if a node has a child.
285f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *
286f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @param tag The tag of an immediate child.
287f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @param tags The tags of lineal descendant.
288f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
289f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public boolean hasChild(int tag, int... tags) throws InvalidAsn1DataException {
290f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        try {
291f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            getChild(tag, tags);
292f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } catch (TagNotFoundException e) {
293f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return false;
294f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
295f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return true;
296f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
297f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
298f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
299f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Gets the first child node having the given {@code tag} and {@code tags}.
300f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *
301f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @param tag The tag of an immediate child.
302f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @param tags The tags of lineal descendant.
303f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws TagNotFoundException If the child cannot be found.
304f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
305f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public Asn1Node getChild(int tag, int... tags)
306f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throws TagNotFoundException, InvalidAsn1DataException {
307f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (!mConstructed) {
308f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new TagNotFoundException(tag);
309f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
310f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        int index = 0;
311f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        Asn1Node node = this;
312f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        while (node != null) {
313f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            List<Asn1Node> children = node.getChildren();
314f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            int size = children.size();
315f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            Asn1Node foundChild = null;
316f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            for (int i = 0; i < size; i++) {
317f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                Asn1Node child = children.get(i);
318f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                if (child.getTag() == tag) {
319f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    foundChild = child;
320f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    break;
321f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                }
322f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
323f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            node = foundChild;
324f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (index >= tags.length) {
325f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                break;
326f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
327f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            tag = tags[index++];
328f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
329f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (node == null) {
330f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new TagNotFoundException(tag);
331f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
332f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return node;
333f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
334f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
335f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
336f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Gets all child nodes which have the given {@code tag}.
337f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *
338f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return If this is primitive or no such children are found, an empty list will be returned.
339f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
340f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public List<Asn1Node> getChildren(int tag)
341f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throws TagNotFoundException, InvalidAsn1DataException {
342f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (!mConstructed) {
343f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return EMPTY_NODE_LIST;
344f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
345f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
346f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        List<Asn1Node> children = getChildren();
347f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (children.isEmpty()) {
348f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return EMPTY_NODE_LIST;
349f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
350f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        List<Asn1Node> output = new ArrayList<>();
351f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        int size = children.size();
352f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        for (int i = 0; i < size; i++) {
353f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            Asn1Node child = children.get(i);
354f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            if (child.getTag() == tag) {
355f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                output.add(child);
356f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
357f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
358f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return output.isEmpty() ? EMPTY_NODE_LIST : output;
359f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
360f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
361f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
362f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Gets all child nodes of this node. If it's a constructed node having encoded data, it's
363f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * children will be decoded here.
364f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *
365f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return If this is primitive, an empty list will be returned. Do not modify the returned list
366f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *     directly.
367f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
368f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public List<Asn1Node> getChildren() throws InvalidAsn1DataException {
369f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (!mConstructed) {
370f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return EMPTY_NODE_LIST;
371f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
372f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
373f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes != null) {
374f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            Asn1Decoder subDecoder = new Asn1Decoder(mDataBytes, mDataOffset, mDataLength);
375f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            while (subDecoder.hasNextNode()) {
376f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                mChildren.add(subDecoder.nextNode());
377f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
378f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            mDataBytes = null;
379f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            mDataOffset = 0;
380f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
381f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return mChildren;
382f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
383f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
384f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** @return Whether this node has a value. False will be returned for a constructed node. */
385f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public boolean hasValue() {
386f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return !mConstructed && mDataBytes != null;
387f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
388f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
389f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
390f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return The data as an integer. If the data length is larger than 4, only the first 4 bytes
391f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *     will be parsed.
392f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
393f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
394f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public int asInteger() throws InvalidAsn1DataException {
395f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mConstructed) {
396f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new IllegalStateException("Cannot get value of a constructed node.");
397f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
398f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes == null) {
399f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
400f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
401f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        try {
402f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return IccUtils.bytesToInt(mDataBytes, mDataOffset, mDataLength);
403f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
404f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
405f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
406f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
407f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
408f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
409f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return The data as a long variable which can be both positive and negative. If the data
410f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *     length is larger than 8, only the first 8 bytes will be parsed.
411f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
412f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
413f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public long asRawLong() throws InvalidAsn1DataException {
414f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mConstructed) {
415f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new IllegalStateException("Cannot get value of a constructed node.");
416f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
417f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes == null) {
418f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
419f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
420f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        try {
421f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return IccUtils.bytesToRawLong(mDataBytes, mDataOffset, mDataLength);
422f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
423f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
424f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
425f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
426f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
427f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
428f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return The data as a string in UTF-8 encoding.
429f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
430f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
431f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public String asString() throws InvalidAsn1DataException {
432f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mConstructed) {
433f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new IllegalStateException("Cannot get value of a constructed node.");
434f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
435f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes == null) {
436f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
437f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
438f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        try {
439f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return new String(mDataBytes, mDataOffset, mDataLength, StandardCharsets.UTF_8);
440f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } catch (IndexOutOfBoundsException e) {
441f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
442f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
443f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
444f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
445f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
446f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return The data as a byte array.
447f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
448f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
449f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public byte[] asBytes() throws InvalidAsn1DataException {
450f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mConstructed) {
451f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new IllegalStateException("Cannot get value of a constructed node.");
452f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
453f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes == null) {
454f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
455f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
456f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        byte[] output = new byte[mDataLength];
457f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        try {
458f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            System.arraycopy(mDataBytes, mDataOffset, output, 0, mDataLength);
459f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } catch (IndexOutOfBoundsException e) {
460f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
461f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
462f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return output;
463f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
464f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
465f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
466f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Gets the data as an integer for BIT STRING. DER actually stores the bits in a reversed order.
467f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * The returned integer here has the order fixed (first bit is at the lowest position). This
468f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * method currently only support at most 32 bits which fit in an integer.
469f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *
470f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return The data as an integer. If this is constructed, a {@code null} will be returned.
471f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
472f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
473f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public int asBits() throws InvalidAsn1DataException {
474f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mConstructed) {
475f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new IllegalStateException("Cannot get value of a constructed node.");
476f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
477f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes == null) {
478f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
479f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
480f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        int bits;
481f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        try {
482f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            bits = IccUtils.bytesToInt(mDataBytes, mDataOffset + 1, mDataLength - 1);
483f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } catch (IllegalArgumentException | IndexOutOfBoundsException e) {
484f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Cannot parse data bytes.", e);
485f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
486f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        for (int i = mDataLength - 1; i < INT_BYTES; i++) {
487f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            bits <<= Byte.SIZE;
488f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
489f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return Integer.reverse(bits);
490f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
491f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
492f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
493f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @return The data as a boolean.
494f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws InvalidAsn1DataException If the data bytes cannot be parsed.
495f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
496f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public boolean asBoolean() throws InvalidAsn1DataException {
497f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mConstructed) {
498f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new IllegalStateException("Cannot get value of a constructed node.");
499f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
500f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes == null) {
501f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(mTag, "Data bytes cannot be null.");
502f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
503f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataLength != 1) {
504f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(
505f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    mTag, "Cannot parse data bytes as boolean: length=" + mDataLength);
506f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
507f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataOffset < 0 || mDataOffset >= mDataBytes.length) {
508f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new InvalidAsn1DataException(
509f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    mTag,
510f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    "Cannot parse data bytes.",
511f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    new ArrayIndexOutOfBoundsException(mDataOffset));
512f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
513f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        // ASN.1 has "true" as 0xFF.
514f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataBytes[mDataOffset] == -1) {
515f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return Boolean.TRUE;
516f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } else if (mDataBytes[mDataOffset] == 0) {
517f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            return Boolean.FALSE;
518f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
519f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        throw new InvalidAsn1DataException(
520f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                mTag, "Cannot parse data bytes as boolean: " + mDataBytes[mDataOffset]);
521f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
522f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
523f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** @return The number of required bytes for encoding this node in DER. */
524f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public int getEncodedLength() {
525f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return mEncodedLength;
526f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
527f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
528f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** @return The number of required bytes for encoding this node's data in DER. */
529f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public int getDataLength() {
530f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return mDataLength;
531f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
532f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
533f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /**
534f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * Writes the DER encoded bytes of this node into a byte array. The number of written bytes is
535f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * {@link #getEncodedLength()}.
536f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     *
537f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     * @throws IndexOutOfBoundsException If the {@code dest} doesn't have enough space to write.
538f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun     */
539f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public void writeToBytes(byte[] dest, int offset) {
540f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (offset < 0 || offset + mEncodedLength > dest.length) {
541f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            throw new IndexOutOfBoundsException(
542f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                    "Not enough space to write. Required bytes: " + mEncodedLength);
543f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
544f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        write(dest, offset);
545f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
546f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
547f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** Writes the DER encoded bytes of this node into a new byte array. */
548f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public byte[] toBytes() {
549f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        byte[] dest = new byte[mEncodedLength];
550f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        write(dest, 0);
551f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return dest;
552f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
553f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
554f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** Gets a hex string representing the DER encoded bytes of this node. */
555f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public String toHex() {
556f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return IccUtils.bytesToHexString(toBytes());
557f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
558f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
559f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** Gets header (tag + length) as hex string. */
560f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    public String getHeadAsHex() {
561f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        String headHex = IccUtils.bytesToHexString(IccUtils.unsignedIntToBytes(mTag));
562f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataLength <= 127) {
563f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            headHex += IccUtils.byteToHex((byte) mDataLength);
564f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } else {
565f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            byte[] lenBytes = IccUtils.unsignedIntToBytes(mDataLength);
566f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            headHex += IccUtils.byteToHex((byte) (lenBytes.length | 0x80));
567f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            headHex += IccUtils.bytesToHexString(lenBytes);
568f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
569f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return headHex;
570f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
571f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun
572f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    /** Returns the new offset where to write the next node data. */
573f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    private int write(byte[] dest, int offset) {
574f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        // Writes the tag.
575f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        offset += IccUtils.unsignedIntToBytes(mTag, dest, offset);
576f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        // Writes the length.
577f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mDataLength <= 127) {
578f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            dest[offset++] = (byte) mDataLength;
579f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } else {
580f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            // Bytes required for encoding the length
581f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            int lenLen = IccUtils.unsignedIntToBytes(mDataLength, dest, ++offset);
582f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            dest[offset - 1] = (byte) (lenLen | 0x80);
583f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            offset += lenLen;
584f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
585f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        // Writes the data.
586f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        if (mConstructed && mDataBytes == null) {
587f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            int size = mChildren.size();
588f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            for (int i = 0; i < size; i++) {
589f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                Asn1Node child = mChildren.get(i);
590f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun                offset = child.write(dest, offset);
591f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            }
592f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        } else if (mDataBytes != null) {
593f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            System.arraycopy(mDataBytes, mDataOffset, dest, offset, mDataLength);
594f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun            offset += mDataLength;
595f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        }
596f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun        return offset;
597f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun    }
598f77821db71423bf8632830d66c0790def700ceeeHolly Jiuyu Sun}
599