• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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