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 com.google.common.annotations.VisibleForTesting; 20 21 import java.math.BigInteger; 22 import java.nio.ByteBuffer; 23 24 import javax.annotation.Nullable; 25 26 /** 27 * Represents the tag of (class and number) of an ASN.1 type. 28 */ 29 public class Asn1Tag { 30 public static final Asn1Tag BOOLEAN = new Asn1Tag(Asn1TagClass.UNIVERSAL, 1); 31 public static final Asn1Tag INTEGER = new Asn1Tag(Asn1TagClass.UNIVERSAL, 2); 32 public static final Asn1Tag BIT_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 3); 33 public static final Asn1Tag OCTET_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 4); 34 public static final Asn1Tag NULL = new Asn1Tag(Asn1TagClass.UNIVERSAL, 5); 35 public static final Asn1Tag OBJECT_IDENTIFIER = new Asn1Tag(Asn1TagClass.UNIVERSAL, 6); 36 public static final Asn1Tag OBJECT_DESCRIPTOR = new Asn1Tag(Asn1TagClass.UNIVERSAL, 7); 37 public static final Asn1Tag REAL = new Asn1Tag(Asn1TagClass.UNIVERSAL, 9); 38 public static final Asn1Tag ENUMERATED = new Asn1Tag(Asn1TagClass.UNIVERSAL, 10); 39 public static final Asn1Tag UTF8STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 12); 40 public static final Asn1Tag SEQUENCE = new Asn1Tag(Asn1TagClass.UNIVERSAL, 16); 41 public static final Asn1Tag SET = new Asn1Tag(Asn1TagClass.UNIVERSAL, 17); 42 public static final Asn1Tag NUMERIC_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 18); 43 public static final Asn1Tag PRINTABLE_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 19); 44 public static final Asn1Tag TELETEX_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 20); 45 public static final Asn1Tag VIDEOTEX_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 21); 46 public static final Asn1Tag IA5_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 22); 47 public static final Asn1Tag UTC_TIME = new Asn1Tag(Asn1TagClass.UNIVERSAL, 23); 48 public static final Asn1Tag GENERALIZED_TIME = new Asn1Tag(Asn1TagClass.UNIVERSAL, 24); 49 public static final Asn1Tag GRAPHIC_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 25); 50 public static final Asn1Tag VISIBLE_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 26); 51 public static final Asn1Tag GENERAL_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 27); 52 public static final Asn1Tag UNIVERSAL_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 28); 53 public static final Asn1Tag BMP_STRING = new Asn1Tag(Asn1TagClass.UNIVERSAL, 30); 54 public static final int CONSTRUCTED_BIT = 0x20; 55 public static final BigInteger MAX_INTEGER_VALUE = BigInteger.valueOf(Integer.MAX_VALUE); 56 57 private final Asn1TagClass tagClass; 58 private final int tagNumber; 59 Asn1Tag(Asn1TagClass tagClass, int tagNumber)60 public Asn1Tag(Asn1TagClass tagClass, int tagNumber) { 61 this.tagClass = tagClass; 62 this.tagNumber = tagNumber; 63 } 64 getTaggedLength(int valueLength)65 public int getTaggedLength(int valueLength) { 66 if (tagNumber > 30) { 67 throw new IllegalArgumentException("Tags with value > 30 not supported"); 68 } 69 return 1 + getLengthLength(valueLength) + valueLength; 70 } 71 72 /** 73 * Returns the number of octets needed for the length field for a value 74 * of the specified size. The returned number will be the smallest possible 75 * number of octets needed for the length field. 76 * 77 * @param valueLength number of octets used to BER encode the value 78 */ getLengthLength(int valueLength)79 static int getLengthLength(int valueLength) { 80 if (valueLength < 0) { 81 throw new IllegalArgumentException("valueLength=" + valueLength); 82 } 83 if (valueLength < (1 << 7)) { 84 // short form 85 return 1; 86 } else if (valueLength < (1 << 8)) { 87 return 2; 88 } else if (valueLength < (1 << 16)) { 89 return 3; 90 } else if (valueLength < (1 << 24)) { 91 return 4; 92 } else { 93 return 5; 94 } 95 } 96 writeTagAndLength(ByteBuffer buf, boolean constructed, int valueLength)97 public void writeTagAndLength(ByteBuffer buf, boolean constructed, int valueLength) { 98 this.writeTag(buf, constructed); 99 writeLength(buf, valueLength); 100 } 101 writeTag(ByteBuffer buf, boolean constructed)102 private void writeTag(ByteBuffer buf, boolean constructed) { 103 if (tagNumber > 30) { 104 throw new IllegalArgumentException("Tags with value > 30 not supported"); 105 } 106 if (constructed) { 107 buf.put((byte) (getValue() | CONSTRUCTED_BIT)); 108 } else { 109 buf.put((byte) getValue()); 110 } 111 } 112 readTag(ByteBuffer buf)113 static Asn1Tag readTag(ByteBuffer buf) { 114 return Asn1Tag.fromValue(buf.get() & 0xFF); 115 } 116 117 /** 118 * Returns the tag at the head of the buffer without advancing the position. 119 */ peekTag(ByteBuffer buf)120 static Asn1Tag peekTag(ByteBuffer buf) { 121 return Asn1Tag.fromValue(buf.get(buf.position()) & 0xFF); 122 } 123 124 /** 125 * Returns the value of the tag octet - including class and tag ID but excluding 126 * the 'C' (composed) bit. 127 */ getValue()128 int getValue() { 129 return (tagClass.getValue() << 6) + tagNumber; 130 } 131 writeLength(ByteBuffer buf, int valueLength)132 static void writeLength(ByteBuffer buf, int valueLength) { 133 if (valueLength < 0) { 134 throw new IllegalArgumentException("valueLength=" + valueLength); 135 } 136 if (valueLength < (1 << 7)) { 137 buf.put((byte) valueLength); 138 } else { 139 // Long form: the low 7 bits of the first octet encodes the number of following 140 // octets that holds the actual length. The top bit is 1 to indicate long form. 141 byte[] lengthBytes = BigInteger.valueOf(valueLength).toByteArray(); 142 if (lengthBytes[0] == (byte) 0x00) { 143 // the length bytes are unsigned so throw away the leading zero octet. 144 // For example, 128 becomes { 0x00, 0x80 } and we drop the first 0x00. 145 int len = lengthBytes.length - 1; 146 byte[] newLengthBytes = new byte[len]; 147 System.arraycopy(lengthBytes, 1, newLengthBytes, 0, len); 148 lengthBytes = newLengthBytes; 149 } 150 buf.put((byte) (lengthBytes.length | (1 << 7))); 151 buf.put(lengthBytes); 152 } 153 } 154 readLength(ByteBuffer buf)155 public static int readLength(ByteBuffer buf) { 156 int n = buf.get() & 0xFF; 157 if (n < (1 << 7)) { 158 // short form of the length field - single octet with high order bit 0 159 return n; 160 } else { 161 // long form - first octet contains number of subsequent octets used to encode the length 162 n = n & 0x7F; 163 if (n > 5) { 164 throw new IllegalArgumentException("Length length too big: " + n + " octets"); 165 } 166 byte[] val = new byte[n]; 167 for (int i = 0; i < n; i++) { 168 val[i] = buf.get(); 169 } 170 val = prependZeroByteIfHighBitSet(val); 171 BigInteger bi = new BigInteger(val); 172 if (bi.compareTo(MAX_INTEGER_VALUE) > 0) { 173 throw new IllegalArgumentException("Lengths bigger than 2^^31-1 unsupported: " + bi); 174 } 175 return bi.intValue(); 176 } 177 } 178 prependZeroByteIfHighBitSet(byte[] ba)179 private static byte[] prependZeroByteIfHighBitSet(byte[] ba) { 180 if ((ba[0] & 0x80) != 0) { 181 byte[] newba = new byte[ba.length + 1]; 182 System.arraycopy(ba, 0, newba, 1, ba.length); 183 newba[0] = 0; 184 ba = newba; 185 } 186 return ba; 187 } 188 189 /** 190 * Returns the tag with the specified value (including tag and length, excluding "constructed" 191 * bit). 192 */ fromValue(int value)193 private static Asn1Tag fromValue(int value) { 194 Asn1Tag result = new Asn1Tag(Asn1TagClass.fromValue(value >> 6), value & 0x1F); 195 if (result.tagNumber > 30) { 196 throw new IllegalArgumentException("Tags with value > 30 not supported (" + result.tagNumber 197 + ")"); 198 } 199 return result; 200 } 201 202 /** 203 * Returns the tag corresponding to the given class and number. 204 * 205 * <p>By convention, null is returned for impossible tag class < 0. Used in code generation. 206 */ fromClassAndNumber(int tagClass, int tagNumber)207 @Nullable public static Asn1Tag fromClassAndNumber(int tagClass, int tagNumber) { 208 if (tagClass < 0) { 209 return null; 210 } 211 return new Asn1Tag(Asn1TagClass.fromValue(tagClass), tagNumber); 212 } 213 214 @Override equals(Object o)215 public boolean equals(Object o) { 216 if (!(o instanceof Asn1Tag)) { 217 return false; 218 } 219 Asn1Tag tag = (Asn1Tag) o; 220 return tagClass == tag.tagClass && tagNumber == tag.tagNumber; 221 } 222 223 @Override hashCode()224 public int hashCode() { 225 int result = 17; 226 result = 31 * result + tagClass.hashCode(); 227 result = 31 * result + tagNumber; 228 return result; 229 } 230 231 @Override toString()232 public String toString() { 233 return "Asn1Tag[" + tagClass + ", " + tagNumber + "]"; 234 } 235 } 236