• 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.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