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 com.android.apksig.internal.asn1; 18 19 import static org.junit.Assert.assertEquals; 20 import static org.junit.Assert.assertNotEquals; 21 import static org.junit.Assert.fail; 22 23 import com.android.apksig.internal.util.HexEncoding; 24 import java.math.BigInteger; 25 import java.nio.ByteBuffer; 26 import java.util.Arrays; 27 import java.util.List; 28 import org.junit.Test; 29 import org.junit.runner.RunWith; 30 import org.junit.runners.JUnit4; 31 32 @RunWith(JUnit4.class) 33 public class Asn1DerEncoderTest { 34 @Test testInteger()35 public void testInteger() throws Exception { 36 assertEquals("3003020100", encodeToHex(new SequenceWithInteger(0))); 37 assertEquals("300302010c", encodeToHex(new SequenceWithInteger(12))); 38 assertEquals("300302017f", encodeToHex(new SequenceWithInteger(0x7f))); 39 assertEquals("3004020200ff", encodeToHex(new SequenceWithInteger(0xff))); 40 assertEquals("30030201ff", encodeToHex(new SequenceWithInteger(-1))); 41 assertEquals("3003020180", encodeToHex(new SequenceWithInteger(-128))); 42 assertEquals("3005020300ffee", encodeToHex(new SequenceWithInteger(0xffee))); 43 assertEquals("300602047fffffff", encodeToHex(new SequenceWithInteger(Integer.MAX_VALUE))); 44 assertEquals("3006020480000000", encodeToHex(new SequenceWithInteger(Integer.MIN_VALUE))); 45 } 46 47 @Test testOctetString()48 public void testOctetString() throws Exception { 49 assertEquals( 50 "30050403010203", 51 encodeToHex( 52 new SequenceWithByteBufferOctetString( 53 ByteBuffer.wrap(new byte[] {1, 2, 3})))); 54 assertEquals( 55 "30030401ff", 56 encodeToHex( 57 new SequenceWithByteBufferOctetString( 58 ByteBuffer.wrap(new byte[] {(byte) 0xff})))); 59 60 assertEquals( 61 "30020400", 62 encodeToHex( 63 new SequenceWithByteBufferOctetString(ByteBuffer.wrap(new byte[0])))); 64 } 65 66 @Test testBitString()67 public void testBitString() throws Exception { 68 assertEquals( 69 "30050303010203", 70 encodeToHex( 71 new SequenceWithByteBufferBitString( 72 ByteBuffer.wrap(new byte[] {1, 2, 3})))); 73 assertEquals( 74 "30030301ff", 75 encodeToHex( 76 new SequenceWithByteBufferBitString( 77 ByteBuffer.wrap(new byte[] {(byte) 0xff})))); 78 79 assertEquals( 80 "30020300", 81 encodeToHex( 82 new SequenceWithByteBufferBitString(ByteBuffer.wrap(new byte[0])))); 83 } 84 85 86 @Test testOid()87 public void testOid() throws Exception { 88 assertEquals("3003060100", encodeToHex(new SequenceWithOid("0.0"))); 89 assertEquals( 90 "300b06092b0601040182371514", 91 encodeToHex(new SequenceWithOid("1.3.6.1.4.1.311.21.20"))); 92 assertEquals( 93 "300b06092a864886f70d010701", 94 encodeToHex(new SequenceWithOid("1.2.840.113549.1.7.1"))); 95 assertEquals( 96 "300b0609608648016503040201", 97 encodeToHex(new SequenceWithOid("2.16.840.1.101.3.4.2.1"))); 98 } 99 100 @Test testChoice()101 public void testChoice() throws Exception { 102 assertEquals("0201ff", encodeToHex(Choice.of(-1))); 103 assertEquals("80092b0601040182371514", encodeToHex(Choice.of("1.3.6.1.4.1.311.21.20"))); 104 } 105 106 @Test(expected = Asn1EncodingException.class) testChoiceWithNoFieldsSet()107 public void testChoiceWithNoFieldsSet() throws Exception { 108 // CHOICE is required to have exactly one field set 109 encode(new Choice(null, null)); 110 } 111 112 @Test(expected = Asn1EncodingException.class) testChoiceWithMultipleFieldsSet()113 public void testChoiceWithMultipleFieldsSet() throws Exception { 114 // CHOICE is required to have exactly one field set 115 encode(new Choice(123, "1.3.6.1.4.1.311.21.20")); 116 } 117 118 @Test testSetOf()119 public void testSetOf() throws Exception { 120 assertEquals("3009310702010a020200ff", encodeToHex(SetOfIntegers.of(0x0a, 0xff))); 121 // Reordering the elements of the set should not make a difference to the resulting encoding 122 assertEquals("3009310702010a020200ff", encodeToHex(SetOfIntegers.of(0xff, 0x0a))); 123 124 assertEquals( 125 "300e310c02010a020200ff0203112233", 126 encodeToHex(SetOfIntegers.of(0xff, 0x0a, 0x112233))); 127 } 128 129 @Test testSequence()130 public void testSequence() throws Exception { 131 assertEquals( 132 "30080201000601000400", 133 encodeToHex(new Sequence(BigInteger.ZERO, "0.0", new byte[0]))); 134 // Optional OBJECT IDENTIFIER not set 135 assertEquals( 136 "30050201000400", 137 encodeToHex(new Sequence(BigInteger.ZERO, null, new byte[0]))); 138 // Required INTEGER not set 139 try { 140 assertEquals( 141 "30050201000400", 142 encodeToHex(new Sequence(null, "0.0", new byte[0]))); 143 fail(); 144 } catch (Asn1EncodingException expected) {} 145 } 146 147 @Test testAsn1Class()148 public void testAsn1Class() throws Exception { 149 assertEquals( 150 "30053003060100", 151 encodeToHex(new SequenceWithAsn1Class(new SequenceWithOid("0.0")))); 152 } 153 154 @Test testOpaque()155 public void testOpaque() throws Exception { 156 assertEquals( 157 "3003060100", 158 encodeToHex(new SequenceWithOpaque( 159 new Asn1OpaqueObject(new byte[] {0x06, 0x01, 0x00})))); 160 } 161 162 @Test testBoolean()163 public void testBoolean() throws Exception { 164 assertEquals("3003010100", encodeToHex(new SequenceWithBoolean(false))); 165 String value = encodeToHex(new SequenceWithBoolean(true)); 166 // The encoding of a true value can be any non-zero value so verify the static portion of 167 // the encoding of a sequeuence with a boolean, then verify the last byte is non-zero 168 assertEquals("The encoding of a sequence with a boolean is not the expected length.", 10, 169 value.length()); 170 assertEquals( 171 "The prefix of the encoding of a sequence with a boolean is not the expected " 172 + "value.", 173 "30030101", value.substring(0, 8)); 174 assertNotEquals("The encoding of true should be non-zero.", "00", value.substring(8)); 175 } 176 177 @Test testUTCTime()178 public void testUTCTime() throws Exception { 179 assertEquals("300d170b313231323231313232315a", 180 encodeToHex(new SequenceWithUTCTime("1212211221Z"))); 181 assertEquals("300d170b393931323331323335395a", 182 encodeToHex(new SequenceWithUTCTime("9912312359Z"))); 183 } 184 185 @Test testGeneralizedTime()186 public void testGeneralizedTime() throws Exception { 187 assertEquals("301518133230313231323231313232302e3939392d3037", 188 encodeToHex(new SequenceWithGeneralizedTime("201212211220.999-07"))); 189 assertEquals("3017181532303338303131393033313430372e3030302b3030", 190 encodeToHex(new SequenceWithGeneralizedTime("20380119031407.000+00"))); 191 } 192 193 @Test testUnencodedContainer()194 public void testUnencodedContainer() throws Exception { 195 assertEquals("30233021310b30030201003004020200ff310830060204800000003108300602047fffffff", 196 encodeToHex( 197 new SequenceWithSequenceOfUnencodedContainers( 198 Arrays.asList( 199 new UnencodedContainerWithSetOfIntegers( 200 Arrays.asList( 201 new SequenceWithInteger(0), 202 new SequenceWithInteger(255))), 203 new UnencodedContainerWithSetOfIntegers( 204 Arrays.asList( 205 new SequenceWithInteger( 206 Integer.MIN_VALUE))), 207 new UnencodedContainerWithSetOfIntegers( 208 Arrays.asList( 209 new SequenceWithInteger( 210 Integer.MAX_VALUE))))))); 211 } 212 encode(Object obj)213 private static byte[] encode(Object obj) throws Asn1EncodingException { 214 return Asn1DerEncoder.encode(obj); 215 } 216 encodeToHex(Object obj)217 private static String encodeToHex(Object obj) throws Asn1EncodingException { 218 return HexEncoding.encode(encode(obj)); 219 } 220 221 222 @Asn1Class(type = Asn1Type.SEQUENCE) 223 public static class SequenceWithInteger { 224 225 @Asn1Field(index = 1, type = Asn1Type.INTEGER) 226 public int num; 227 SequenceWithInteger(int num)228 public SequenceWithInteger(int num) { 229 this.num = num; 230 } 231 } 232 233 @Asn1Class(type = Asn1Type.SEQUENCE) 234 public static class SequenceWithOid { 235 236 @Asn1Field(index = 1, type = Asn1Type.OBJECT_IDENTIFIER) 237 public String oid; 238 SequenceWithOid(String oid)239 public SequenceWithOid(String oid) { 240 this.oid = oid; 241 } 242 } 243 244 @Asn1Class(type = Asn1Type.SEQUENCE) 245 public static class SequenceWithByteBufferOctetString { 246 247 @Asn1Field(index = 1, type = Asn1Type.OCTET_STRING) 248 public ByteBuffer data; 249 SequenceWithByteBufferOctetString(ByteBuffer data)250 public SequenceWithByteBufferOctetString(ByteBuffer data) { 251 this.data = data; 252 } 253 } 254 255 @Asn1Class(type = Asn1Type.SEQUENCE) 256 public static class SequenceWithByteBufferBitString { 257 258 @Asn1Field(index = 1, type = Asn1Type.BIT_STRING) 259 public ByteBuffer data; 260 SequenceWithByteBufferBitString(ByteBuffer data)261 public SequenceWithByteBufferBitString(ByteBuffer data) { 262 this.data = data; 263 } 264 } 265 266 @Asn1Class(type = Asn1Type.CHOICE) 267 public static class Choice { 268 269 @Asn1Field(type = Asn1Type.INTEGER) 270 public Integer num; 271 272 @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0) 273 public String oid; 274 Choice(Integer num, String oid)275 public Choice(Integer num, String oid) { 276 this.num = num; 277 this.oid = oid; 278 } 279 of(int num)280 public static Choice of(int num) { 281 return new Choice(num, null); 282 } 283 of(String oid)284 public static Choice of(String oid) { 285 return new Choice(null, oid); 286 } 287 } 288 289 @Asn1Class(type = Asn1Type.SEQUENCE) 290 public static class SetOfIntegers { 291 @Asn1Field(type = Asn1Type.SET_OF, elementType = Asn1Type.INTEGER) 292 public List<Integer> values; 293 of(Integer... values)294 public static SetOfIntegers of(Integer... values) { 295 SetOfIntegers result = new SetOfIntegers(); 296 result.values = Arrays.asList(values); 297 return result; 298 } 299 } 300 301 @Asn1Class(type = Asn1Type.SEQUENCE) 302 public static class Sequence { 303 @Asn1Field(type = Asn1Type.INTEGER, index = 0) 304 public BigInteger num; 305 306 @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER, index = 1, optional = true) 307 public String oid; 308 309 @Asn1Field(type = Asn1Type.OCTET_STRING, index = 2) 310 public byte[] octets; 311 Sequence(BigInteger num, String oid, byte[] octets)312 public Sequence(BigInteger num, String oid, byte[] octets) { 313 this.num = num; 314 this.oid = oid; 315 this.octets = octets; 316 } 317 } 318 319 @Asn1Class(type = Asn1Type.SEQUENCE) 320 public static class SequenceWithAsn1Class { 321 @Asn1Field(type = Asn1Type.SEQUENCE) 322 public SequenceWithOid seqWithOid; 323 SequenceWithAsn1Class(SequenceWithOid seqWithOid)324 public SequenceWithAsn1Class(SequenceWithOid seqWithOid) { 325 this.seqWithOid = seqWithOid; 326 } 327 } 328 329 @Asn1Class(type = Asn1Type.SEQUENCE) 330 public static class SequenceWithOpaque { 331 @Asn1Field(type = Asn1Type.ANY) 332 public Asn1OpaqueObject obj; 333 SequenceWithOpaque(Asn1OpaqueObject obj)334 public SequenceWithOpaque(Asn1OpaqueObject obj) { 335 this.obj = obj; 336 } 337 } 338 339 @Asn1Class(type = Asn1Type.SEQUENCE) 340 public static class SequenceWithBoolean { 341 342 @Asn1Field(index = 1, type = Asn1Type.BOOLEAN) 343 public boolean value; 344 SequenceWithBoolean(boolean value)345 public SequenceWithBoolean(boolean value) { 346 this.value = value; 347 } 348 } 349 350 @Asn1Class(type = Asn1Type.SEQUENCE) 351 public static class SequenceWithUTCTime { 352 353 @Asn1Field(index = 1, type = Asn1Type.UTC_TIME) 354 public String utcTime; 355 SequenceWithUTCTime(String utcTime)356 public SequenceWithUTCTime(String utcTime) { 357 this.utcTime = utcTime; 358 } 359 } 360 361 @Asn1Class(type = Asn1Type.SEQUENCE) 362 public static class SequenceWithGeneralizedTime { 363 364 @Asn1Field(index = 1, type = Asn1Type.GENERALIZED_TIME) 365 public String generalizedTime; 366 SequenceWithGeneralizedTime(String generalizedTime)367 public SequenceWithGeneralizedTime(String generalizedTime) { 368 this.generalizedTime = generalizedTime; 369 } 370 } 371 372 @Asn1Class(type = Asn1Type.SEQUENCE) 373 public static class SequenceWithSequenceOfUnencodedContainers { 374 @Asn1Field(index = 1, type = Asn1Type.SEQUENCE_OF) 375 public List<UnencodedContainerWithSetOfIntegers> containers; 376 SequenceWithSequenceOfUnencodedContainers( List<UnencodedContainerWithSetOfIntegers> containers)377 public SequenceWithSequenceOfUnencodedContainers( 378 List<UnencodedContainerWithSetOfIntegers> containers) { 379 this.containers = containers; 380 } 381 } 382 383 @Asn1Class(type = Asn1Type.UNENCODED_CONTAINER) 384 public static class UnencodedContainerWithSetOfIntegers { 385 @Asn1Field(index = 1, type = Asn1Type.SET_OF) 386 public List<SequenceWithInteger> values; 387 UnencodedContainerWithSetOfIntegers(List<SequenceWithInteger> values)388 public UnencodedContainerWithSetOfIntegers(List<SequenceWithInteger> values) { 389 this.values = values; 390 } 391 } 392 } 393