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.assertNotNull; 21 import static org.junit.Assert.assertNull; 22 import static org.junit.Assert.fail; 23 24 import com.android.apksig.internal.util.HexEncoding; 25 import java.math.BigInteger; 26 import java.nio.ByteBuffer; 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 Asn1BerParserTest { 34 @Test(expected = NullPointerException.class) testNullInput()35 public void testNullInput() throws Exception { 36 parse((ByteBuffer) null, EmptySequence.class); 37 } 38 39 @Test(expected = Asn1DecodingException.class) testEmptyInput()40 public void testEmptyInput() throws Exception { 41 parse("", EmptySequence.class); 42 } 43 44 @Test testEmptySequence()45 public void testEmptySequence() throws Exception { 46 // Empty SEQUENCE (0x3000) followed by garbage (0x12345678) 47 ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("300012345678")); 48 EmptySequence container = parse(input, EmptySequence.class); 49 assertNotNull(container); 50 // Check that input position has been advanced appropriately 51 assertEquals(2, input.position()); 52 } 53 54 @Test testOctetString()55 public void testOctetString() throws Exception { 56 assertEquals( 57 "123456", 58 HexEncoding.encode(parse("30050403123456", SequenceWithOctetString.class).buf)); 59 assertEquals( 60 "", HexEncoding.encode(parse("30020400", SequenceWithOctetString.class).buf)); 61 } 62 63 @Test testBitString()64 public void testBitString() throws Exception { 65 assertEquals( 66 "123456", 67 HexEncoding.encode(parse("30050303123456", SequenceWithBitString.class).buf)); 68 assertEquals( 69 "", HexEncoding.encode(parse("30020300", SequenceWithBitString.class).buf)); 70 } 71 72 @Test testBoolean()73 public void testBoolean() throws Exception { 74 assertEquals(false, parse("3003010100", SequenceWithBoolean.class).value); 75 assertEquals(true, parse("3003010101", SequenceWithBoolean.class).value); 76 assertEquals(true, parse("30030101FF", SequenceWithBoolean.class).value); 77 } 78 79 @Test testUTCTime()80 public void testUTCTime() throws Exception { 81 assertEquals("1212211221Z", 82 parse("300d170b313231323231313232315a", SequenceWithUTCTime.class).value); 83 assertEquals("9912312359Z", 84 parse("300d170b393931323331323335395a", SequenceWithUTCTime.class).value); 85 } 86 87 @Test testGeneralizedTime()88 public void testGeneralizedTime() throws Exception { 89 assertEquals("201212211220.999-07", parse("301518133230313231323231313232302e3939392d3037", 90 SequenceWithGeneralizedTime.class).value); 91 assertEquals("20380119031407.000+00", 92 parse("3017181532303338303131393033313430372e3030302b3030", 93 SequenceWithGeneralizedTime.class).value); 94 } 95 96 @Test testInteger()97 public void testInteger() throws Exception { 98 // Various Java types decoded from INTEGER 99 // Empty SEQUENCE (0x3000) followed by garbage (0x12345678) 100 SequenceWithIntegers container = 101 parse("301e" 102 + "0201ff" // -1 103 + "0207ff123456789abc" // -7f123456789abc 104 + "0200" // 0 105 + "020280ff" // -255 106 + "020a00000000000000001234", // 0x1234 107 SequenceWithIntegers.class); 108 assertEquals(-1, container.n1); 109 } 110 111 @Test testOid()112 public void testOid() throws Exception { 113 // Empty OID 114 try { 115 parse("30020600", SequenceWithOid.class); 116 fail(); 117 } catch (Asn1DecodingException expected) {} 118 119 120 assertEquals("2.100.3", parse("30050603813403", SequenceWithOid.class).oid); 121 assertEquals( 122 "2.16.840.1.101.3.4.2.1", 123 parse("300b0609608648016503040201", SequenceWithOid.class).oid); 124 } 125 126 @Test testSequenceOf()127 public void testSequenceOf() throws Exception { 128 assertEquals(2, parse("3006300430003000", SequenceWithSequenceOf.class).values.size()); 129 } 130 131 @Test testSetOf()132 public void testSetOf() throws Exception { 133 assertEquals(2, parse("3006310430003000", SequenceWithSetOf.class).values.size()); 134 } 135 136 @Test testUnencodedContainer()137 public void testUnencodedContainer() throws Exception { 138 SequenceWithSequenceOfUnencodedContainers seq = parse("300C300A31023000310430003000", 139 SequenceWithSequenceOfUnencodedContainers.class); 140 assertEquals(2, seq.containers.size()); 141 assertEquals(1, seq.containers.get(0).values.size()); 142 assertEquals(2, seq.containers.get(1).values.size()); 143 } 144 145 @Test testImplicitOptionalField()146 public void testImplicitOptionalField() throws Exception { 147 // Optional field f2 missing in the input 148 SequenceWithImplicitOptionalField seq = 149 parse("300602010d02012a", SequenceWithImplicitOptionalField.class); 150 assertEquals(13, seq.f1.intValue()); 151 assertNull(seq.f2); 152 assertEquals(42, seq.f3.intValue()); 153 154 // Optional field f2 present in the input 155 seq = parse("300a02010da102ffff02012a", SequenceWithImplicitOptionalField.class); 156 assertEquals(13, seq.f1.intValue()); 157 assertEquals(-1, seq.f2.intValue()); 158 assertEquals(42, seq.f3.intValue()); 159 } 160 161 162 @Test testExplicitOptionalField()163 public void testExplicitOptionalField() throws Exception { 164 // Optional field f2 missing in the input 165 SequenceWithExplicitOptionalField seq = 166 parse("300602010d02012a", SequenceWithExplicitOptionalField.class); 167 assertEquals(13, seq.f1.intValue()); 168 assertNull(seq.f2); 169 assertEquals(42, seq.f3.intValue()); 170 171 // Optional field f2 present in the input 172 seq = parse("300c02010da1040202ffff02012a", SequenceWithExplicitOptionalField.class); 173 assertEquals(13, seq.f1.intValue()); 174 assertEquals(-1, seq.f2.intValue()); 175 assertEquals(42, seq.f3.intValue()); 176 } 177 178 @Test testChoiceWithDifferentTypedOptions()179 public void testChoiceWithDifferentTypedOptions() throws Exception { 180 // The CHOICE can be either an INTEGER or an OBJECT IDENTIFIER 181 182 // INTEGER 183 ChoiceWithTwoOptions c = parse("0208ffffffffffffffff", ChoiceWithTwoOptions.class); 184 assertNull(c.oid); 185 assertEquals(-1, c.num.intValue()); 186 187 // OBJECT IDENTIFIER 188 c = parse("060100", ChoiceWithTwoOptions.class); 189 assertEquals("0.0", c.oid); 190 assertNull(c.num); 191 192 // Empty input 193 try { 194 parse("", ChoiceWithTwoOptions.class); 195 fail(); 196 } catch (Asn1DecodingException expected) {} 197 198 // Neither of the options match 199 try { 200 // Empty SEQUENCE 201 parse("3000", ChoiceWithTwoOptions.class); 202 fail(); 203 } catch (Asn1DecodingException expected) {} 204 } 205 206 @Test testChoiceWithSameTypedOptions()207 public void testChoiceWithSameTypedOptions() throws Exception { 208 // The CHOICE can be either a SEQUENCE, an IMPLICIT SEQUENCE, or an EXPLICIT SEQUENCE 209 210 // SEQUENCE 211 ChoiceWithThreeSequenceOptions c = parse("3000", ChoiceWithThreeSequenceOptions.class); 212 assertNotNull(c.s1); 213 assertNull(c.s2); 214 assertNull(c.s3); 215 216 // IMPLICIT [0] SEQUENCE 217 c = parse("a000", ChoiceWithThreeSequenceOptions.class); 218 assertNull(c.s1); 219 assertNotNull(c.s2); 220 assertNull(c.s3); 221 222 // EXPLICIT [0] SEQUENCE 223 c = parse("a1023000", ChoiceWithThreeSequenceOptions.class); 224 assertNull(c.s1); 225 assertNull(c.s2); 226 assertNotNull(c.s3); 227 228 // INTEGER -- None of the options match 229 try { 230 parse("02010a", ChoiceWithThreeSequenceOptions.class); 231 fail(); 232 } catch (Asn1DecodingException expected) {} 233 } 234 235 @Test(expected = Asn1DecodingException.class) testChoiceWithClashingOptions()236 public void testChoiceWithClashingOptions() throws Exception { 237 // The CHOICE is between INTEGER and INTEGER which clash 238 parse("0200", ChoiceWithClashingOptions.class); 239 } 240 241 @Test testPrimitiveIndefiniteLengthEncodingWithGarbage()242 public void testPrimitiveIndefiniteLengthEncodingWithGarbage() throws Exception { 243 // Indefinite length INTEGER containing what may look like a malformed definite length 244 // INTEGER, followed by an INTEGER. This tests that contents of indefinite length encoded 245 // primitive (i.e., not constructed) data values must not be parsed to locate the 0x00 0x00 246 // terminator. 247 ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("0280020401000002010c")); 248 ChoiceWithTwoOptions c = parse(input, ChoiceWithTwoOptions.class); 249 // Check what's remaining in the input buffer 250 assertEquals("02010c", HexEncoding.encode(input)); 251 // Check what was consumed 252 assertEquals(0x020401, c.num.intValue()); 253 254 // Indefinite length INTEGER containing what may look like a malformed indefinite length 255 // INTEGER, followed by an INTEGER 256 input = ByteBuffer.wrap(HexEncoding.decode("0280028001000002010c")); 257 c = parse(input, ChoiceWithTwoOptions.class); 258 // Check what's remaining in the input buffer 259 assertEquals("02010c", HexEncoding.encode(input)); 260 // Check what was consumed 261 assertEquals(0x028001, c.num.intValue()); 262 } 263 264 @Test testConstructedIndefiniteLengthEncodingWithoutNestedIndefiniteLengthDataValues()265 public void testConstructedIndefiniteLengthEncodingWithoutNestedIndefiniteLengthDataValues() 266 throws Exception { 267 // Indefinite length SEQUENCE containing an INTEGER whose encoding contains 0x00 0x00 which 268 // can be misinterpreted as indefinite length encoding terminator of the SEQUENCE, followed 269 // by an INTEGER 270 ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("308002020000000002010c")); 271 SequenceWithAsn1Opaque c = parse(input, SequenceWithAsn1Opaque.class); 272 // Check what's remaining in the input buffer 273 assertEquals("02010c", HexEncoding.encode(input)); 274 // Check what was read 275 assertEquals("02020000", HexEncoding.encode(c.obj.getEncoded())); 276 } 277 278 @Test testConstructedIndefiniteLengthEncodingWithNestedIndefiniteLengthDataValues()279 public void testConstructedIndefiniteLengthEncodingWithNestedIndefiniteLengthDataValues() 280 throws Exception { 281 // Indefinite length SEQUENCE containing two INTEGER fields using indefinite 282 // length encoding, followed by an INTEGER. This tests that the 0x00 0x00 terminators used 283 // by the encoding of the two INTEGERs are not confused for the 0x00 0x00 terminator of the 284 // SEQUENCE. 285 ByteBuffer input = 286 ByteBuffer.wrap(HexEncoding.decode("308002800300000280030000020103000002010c")); 287 SequenceWithAsn1Opaque c = parse(input, SequenceWithAsn1Opaque.class); 288 // Check what's remaining in the input buffer 289 assertEquals("02010c", HexEncoding.encode(input)); 290 // Check what was consumed 291 assertEquals("0280030000", HexEncoding.encode(c.obj.getEncoded())); 292 } 293 294 @Test(expected = Asn1DecodingException.class) testConstructedIndefiniteLengthEncodingWithGarbage()295 public void testConstructedIndefiniteLengthEncodingWithGarbage() throws Exception { 296 // Indefinite length SEQUENCE containing an indefinite length encoded SEQUENCE containing 297 // garbage which doesn't parse as BER, followed by an INTEGER. This tests that contents of 298 // the SEQUENCEs must be parsed to establish where their 0x00 0x00 terminators are located. 299 ByteBuffer input = ByteBuffer.wrap(HexEncoding.decode("3080308002040000000002010c")); 300 parse(input, SequenceWithAsn1Opaque.class); 301 } 302 parse(String hexEncodedInput, Class<T> containerClass)303 private static <T> T parse(String hexEncodedInput, Class<T> containerClass) 304 throws Asn1DecodingException { 305 ByteBuffer input = 306 (hexEncodedInput == null) 307 ? null : ByteBuffer.wrap(HexEncoding.decode(hexEncodedInput)); 308 return parse(input, containerClass); 309 } 310 parse(ByteBuffer input, Class<T> containerClass)311 private static <T> T parse(ByteBuffer input, Class<T> containerClass) 312 throws Asn1DecodingException { 313 return Asn1BerParser.parse(input, containerClass); 314 } 315 316 @Asn1Class(type = Asn1Type.SEQUENCE) 317 public static class EmptySequence {} 318 319 @Asn1Class(type = Asn1Type.SEQUENCE) 320 public static class SequenceWithIntegers { 321 @Asn1Field(index = 1, type = Asn1Type.INTEGER) 322 public int n1; 323 324 @Asn1Field(index = 2, type = Asn1Type.INTEGER) 325 public long n2; 326 327 @Asn1Field(index = 3, type = Asn1Type.INTEGER) 328 public Integer n3; 329 330 @Asn1Field(index = 4, type = Asn1Type.INTEGER) 331 public Long n4; 332 333 @Asn1Field(index = 5, type = Asn1Type.INTEGER) 334 public BigInteger n5; 335 } 336 337 @Asn1Class(type = Asn1Type.SEQUENCE) 338 public static class SequenceWithOid { 339 @Asn1Field(index = 0, type = Asn1Type.OBJECT_IDENTIFIER) 340 public String oid; 341 } 342 343 @Asn1Class(type = Asn1Type.SEQUENCE) 344 public static class SequenceWithImplicitOptionalField { 345 @Asn1Field(index = 1, type = Asn1Type.INTEGER) 346 public Integer f1; 347 348 @Asn1Field(index = 2, type = Asn1Type.INTEGER, optional = true, 349 tagging = Asn1Tagging.IMPLICIT, tagNumber = 1) 350 public Integer f2; 351 352 @Asn1Field(index = 3, type = Asn1Type.INTEGER) 353 public Integer f3; 354 } 355 356 @Asn1Class(type = Asn1Type.SEQUENCE) 357 public static class SequenceWithExplicitOptionalField { 358 @Asn1Field(index = 1, type = Asn1Type.INTEGER) 359 public Integer f1; 360 361 @Asn1Field(index = 2, type = Asn1Type.INTEGER, optional = true, 362 tagging = Asn1Tagging.EXPLICIT, tagNumber = 1) 363 public Integer f2; 364 365 @Asn1Field(index = 3, type = Asn1Type.INTEGER) 366 public Integer f3; 367 } 368 369 @Asn1Class(type = Asn1Type.CHOICE) 370 public static class ChoiceWithTwoOptions { 371 @Asn1Field(type = Asn1Type.OBJECT_IDENTIFIER) 372 public String oid; 373 374 @Asn1Field(type = Asn1Type.INTEGER) 375 public Integer num; 376 } 377 378 @Asn1Class(type = Asn1Type.CHOICE) 379 public static class ChoiceWithThreeSequenceOptions { 380 @Asn1Field(type = Asn1Type.SEQUENCE) 381 public EmptySequence s1; 382 383 @Asn1Field(type = Asn1Type.SEQUENCE, tagging = Asn1Tagging.IMPLICIT, tagNumber = 0) 384 public EmptySequence s2; 385 386 @Asn1Field(type = Asn1Type.SEQUENCE, tagging = Asn1Tagging.EXPLICIT, tagNumber = 1) 387 public EmptySequence s3; 388 } 389 390 @Asn1Class(type = Asn1Type.CHOICE) 391 public static class ChoiceWithClashingOptions { 392 @Asn1Field(type = Asn1Type.INTEGER) 393 public int n1; 394 395 @Asn1Field(type = Asn1Type.INTEGER) 396 public Integer n2; 397 } 398 399 @Asn1Class(type = Asn1Type.SEQUENCE) 400 public static class SequenceWithOctetString { 401 @Asn1Field(index = 0, type = Asn1Type.OCTET_STRING) 402 public ByteBuffer buf; 403 } 404 405 @Asn1Class(type = Asn1Type.SEQUENCE) 406 public static class SequenceWithBitString { 407 @Asn1Field(index = 0, type = Asn1Type.BIT_STRING) 408 public ByteBuffer buf; 409 } 410 411 @Asn1Class(type = Asn1Type.SEQUENCE) 412 public static class SequenceWithSequenceOf { 413 @Asn1Field(index = 0, type = Asn1Type.SEQUENCE_OF) 414 public List<EmptySequence> values; 415 } 416 417 @Asn1Class(type = Asn1Type.SEQUENCE) 418 public static class SequenceWithSetOf { 419 @Asn1Field(index = 0, type = Asn1Type.SET_OF) 420 public List<EmptySequence> values; 421 } 422 423 @Asn1Class(type = Asn1Type.SEQUENCE) 424 public static class SequenceWithAsn1Opaque { 425 @Asn1Field(type = Asn1Type.ANY) 426 public Asn1OpaqueObject obj; 427 } 428 429 @Asn1Class(type = Asn1Type.SEQUENCE) 430 public static class SequenceWithSequenceOfUnencodedContainers { 431 @Asn1Field(type = Asn1Type.SEQUENCE_OF) 432 public List<UnencodedContainerWithSetOf> containers; 433 } 434 435 @Asn1Class(type = Asn1Type.UNENCODED_CONTAINER) 436 public static class UnencodedContainerWithSetOf { 437 @Asn1Field(type = Asn1Type.SET_OF) 438 public List<EmptySequence> values; 439 } 440 441 @Asn1Class(type = Asn1Type.SEQUENCE) 442 public static class SequenceWithBoolean { 443 @Asn1Field(type = Asn1Type.BOOLEAN) 444 public boolean value; 445 } 446 447 @Asn1Class(type = Asn1Type.SEQUENCE) 448 public static class SequenceWithUTCTime { 449 @Asn1Field(type = Asn1Type.UTC_TIME) 450 public String value; 451 } 452 453 @Asn1Class(type = Asn1Type.SEQUENCE) 454 public static class SequenceWithGeneralizedTime { 455 @Asn1Field(type = Asn1Type.GENERALIZED_TIME) 456 public String value; 457 } 458 } 459