1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.location.cts.asn1.base;
18 
19 import java.nio.BufferOverflowException;
20 import java.nio.ByteBuffer;
21 import java.nio.ReadOnlyBufferException;
22 import java.util.Collection;
23 
24 import javax.annotation.Nullable;
25 
26 /**
27  * Specifies ASN.1 functionality needed for all ASN.1 objects.
28  *
29  */
30 public abstract class Asn1Object {
31   /**
32    * Returns the tags that can decode to this type. Subclasses are expected to "fauxverride".
33    */
getPossibleFirstTags()34   public static Collection<Asn1Tag> getPossibleFirstTags() {
35     throw new UnsupportedOperationException("No first tags defined");
36   }
37 
38   /**
39    * Returns the universal tag defined for the base type.
40    */
getDefaultTag()41   abstract Asn1Tag getDefaultTag();
42 
43   /**
44    * Returns the declared tag for this type. Tagged subclasses must override, the
45    * default implementation returns null.
46    */
getTag()47   @Nullable protected Asn1Tag getTag() {
48     return null;
49   }
50 
51   /**
52    * Returns true if implicit tagging is to be applied to the type. Tagged subclasses must
53    * override, the default implementation throws.
54    */
isTagImplicit()55   protected boolean isTagImplicit() {
56     throw new IllegalStateException("Not a tagged subclass");
57   }
58 
59   /**
60    * Returns true if this object is "constructed", that is, it's composed of further TLVs
61    * (as opposed to being primitive).
62    */
isConstructed()63   boolean isConstructed() {
64     return false;
65   }
66 
67   /**
68    * Returns the number of octets in the BER encoding of this object. Called by
69    * {@link #encodeBer()} to allocate a buffer of the correct size before starting
70    * the encoding.
71    */
getBerLength()72   int getBerLength() {
73     int valueLen = getBerValueLength();
74     Asn1Tag tag = getTag();
75     if (tag == null) {
76       tag = getDefaultTag();
77     } else if (!isTagImplicit()) {
78       valueLen = getDefaultTag().getTaggedLength(valueLen);
79     }
80     return tag.getTaggedLength(valueLen);
81   }
82 
83   /**
84    * Returns the number of octets required to encoded the value of this {@link Asn1Object}.
85    */
getBerValueLength()86   abstract int getBerValueLength();
87 
88   /**
89    * Returns the BER encoding of this object.
90    */
encodeBer()91   public byte[] encodeBer() {
92     ByteBuffer buf = ByteBuffer.allocate(getBerLength());
93     encodeBer(buf);
94     return buf.array();
95   }
96 
encodeBer(ByteBuffer buf)97   void encodeBer(ByteBuffer buf) {
98     int valueLen = getBerValueLength();
99     Asn1Tag tag = getTag();
100     if (tag == null) {
101       tag = getDefaultTag();
102     } else {
103       if (!isTagImplicit()) {
104         int innerValueLen = getDefaultTag().getTaggedLength(valueLen);
105         tag.writeTagAndLength(buf, true, innerValueLen);
106         tag = getDefaultTag();
107       }
108     }
109     tag.writeTagAndLength(buf, isConstructed(), valueLen);
110     encodeBerValue(buf);
111   }
112 
113   /**
114    * Writes the BER encoded value of this {@link Asn1Object} into the specified buffer.
115    *
116    * @param buf the byte buffer to write to
117    * @throws BufferOverflowException If there is insufficient space in the buffer
118    * @throws ReadOnlyBufferException If the buffer is read-only
119    */
encodeBerValue(ByteBuffer buf)120   abstract void encodeBerValue(ByteBuffer buf);
121 
122   /**
123    * BER decodes the provided buffer. Should only be called on newly created instances as subclasses
124    * may not write optional fields not explicitly present in the input.
125    *
126    * @param data the BER encoded input
127    * @throws IllegalArgumentException in case of parsing failure
128    */
decodeBer(byte[] data)129   public void decodeBer(byte[] data) {
130     decodeBer(ByteBuffer.wrap(data));
131   }
132 
decodeBer(ByteBuffer buf)133   void decodeBer(ByteBuffer buf) {
134     Asn1Tag tag = Asn1Tag.readTag(buf);
135     int valueLen = Asn1Tag.readLength(buf);
136 
137     if (valueLen != buf.remaining()) {
138       throw new IllegalArgumentException("Length mismatch: expected=" + buf.remaining()
139           + ", actual=" + valueLen);
140     }
141 
142     if (getTag() != null) {
143       checkTag(tag, getTag());
144       if (!isTagImplicit()) {
145         // read inner tag and length
146         Asn1Tag innerTag = Asn1Tag.readTag(buf);
147         int innerValueLen = Asn1Tag.readLength(buf);
148         if (innerValueLen != buf.remaining()) {
149           throw new IllegalArgumentException("Length mismatch: expected=" + buf.remaining()
150               + ", actual=" + innerValueLen);
151         }
152         checkTag(innerTag, getDefaultTag());
153       }
154     }
155 
156     decodeBerValue(buf);
157 
158     if (buf.hasRemaining()) {
159       throw new IllegalArgumentException("BER encoded input not fully read");
160     }
161   }
162 
checkTag(Asn1Tag actual, Asn1Tag expected)163   void checkTag(Asn1Tag actual, Asn1Tag expected) {
164     if (!expected.equals(actual)) {
165       throw new IllegalArgumentException("Invalid tag: expected=" + expected
166           + ", actual=" + actual);
167     }
168   }
169 
170   /**
171    * Decodes the BER encoded value of this {@link Asn1Object}. On return from this method the
172    * provided buffer should be empty.
173    */
decodeBerValue(ByteBuffer buf)174   abstract void decodeBerValue(ByteBuffer buf);
175 
176   /**
177    * Returns remaining bytes in the {@link ByteBuffer} in a newly allocated byte array of exactly
178    * the right size. The ByteBuffer will be empty upon return.
179    */
getRemaining(ByteBuffer buf)180   static byte[] getRemaining(ByteBuffer buf) {
181     byte[] bytes = new byte[buf.remaining()];
182     buf.get(bytes);
183     return bytes;
184   }
185 
encodePerAligned()186   public abstract Iterable<BitStream> encodePerAligned();
187 
encodePerUnaligned()188   public abstract Iterable<BitStream> encodePerUnaligned();
189 
190   /**
191    * This method should only be called on a newly created instance to avoid
192    * having residue state in it.
193    */
decodePerUnaligned(BitStreamReader reader)194   public abstract void decodePerUnaligned(BitStreamReader reader);
195 
196 
197   /**
198    * This method should only be called on a newly created instance to avoid
199    * having residue state in it.
200    */
decodePerAligned(BitStreamReader reader)201   public abstract void decodePerAligned(BitStreamReader reader);
202 
toIndentedString(String indent)203   public String toIndentedString(String indent) {
204     return indent + toString();
205   }
206 }
207