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 static android.location.cts.asn1.base.PerAlignedUtils.SIXTYFOUR_K;
20 
21 import com.google.common.base.Preconditions;
22 import com.google.common.collect.ImmutableList;
23 
24 import java.math.BigInteger;
25 import java.nio.ByteBuffer;
26 import java.util.Collection;
27 import java.util.Objects;
28 
29 import javax.annotation.Nullable;
30 
31 /**
32  * Implements ASN.1 functionality.
33  *
34  */
35 public class Asn1Integer extends Asn1Object {
36   private static final Collection<Asn1Tag> possibleFirstTags =
37       ImmutableList.of(Asn1Tag.INTEGER);
38 
39   private BigInteger minimumValue = null; // null == unbounded.
40   private BigInteger maximumValue = null; // null == unbounded.
41   private BigInteger value;
42 
getPossibleFirstTags()43   public static Collection<Asn1Tag> getPossibleFirstTags() {
44     return possibleFirstTags;
45   }
46 
getDefaultTag()47   @Override Asn1Tag getDefaultTag() {
48     return Asn1Tag.INTEGER;
49   }
50 
51   /**
52    * Sets the allowed range of values. A null for either parameter means that
53    * the value is unbounded in that direction.
54    */
setValueRange(@ullable String minimum, @Nullable String maximum)55   protected void setValueRange(@Nullable String minimum,
56                                @Nullable String maximum) {
57     minimumValue = minimum == null ? null : new BigInteger(minimum);
58     maximumValue = maximum == null ? null : new BigInteger(maximum);
59   }
60 
encodeNormalizedIntegerWithRangeAligned( BigInteger normalizedValue, BigInteger range)61   private Iterable<BitStream> encodeNormalizedIntegerWithRangeAligned(
62       BigInteger normalizedValue, BigInteger range) {
63     if (range.compareTo(BigInteger.valueOf(SIXTYFOUR_K)) < 0) {
64       BitStream result = PerAlignedUtils.encodeNormalizedSmallConstrainedWholeNumber(
65           normalizedValue.intValue(), range.intValue());
66       return ImmutableList.of(result);
67     } else {
68       return PerAlignedUtils.encodeConstrainedLengthOfBytes(
69           PerAlignedUtils.encodeBigNonNegativeWholeNumber(normalizedValue),
70           1,
71           PerAlignedUtils.encodeBigNonNegativeWholeNumber(range).length);
72     }
73   }
74 
encodeNormalizedIntegerWithRangeUnaligned( BigInteger normalizedValue, BigInteger range)75   private Iterable<BitStream> encodeNormalizedIntegerWithRangeUnaligned(
76       BigInteger normalizedValue, BigInteger range) {
77     BitStream result = PerUnalignedUtils.encodeNormalizedConstrainedWholeNumber(
78         normalizedValue.longValue(), range.longValue());
79     return ImmutableList.of(result);
80   }
81 
validateValue()82   private void validateValue() {
83     Objects.requireNonNull(value, "No value set.");
84     Preconditions.checkState(
85         minimumValue == null || value.compareTo(minimumValue) >= 0,
86         "Too small value %s", value);
87     Preconditions.checkState(
88         maximumValue == null || value.compareTo(maximumValue) <= 0,
89         "Too large value %s", value);
90   }
91 
getBerValueLength()92    @Override int getBerValueLength() {
93     if (value.equals(BigInteger.ZERO)) {
94       // BER requires 0 be encoded with one or more zero octets
95       return 1;
96     } else {
97       return (value.bitLength() >> 3) + 1;
98     }
99   }
100 
encodeBerValue(ByteBuffer buf)101   @Override void encodeBerValue(ByteBuffer buf) {
102     if (value.equals(BigInteger.ZERO)) {
103       buf.put((byte) 0);
104     } else {
105       buf.put(value.toByteArray());
106     }
107   }
108 
decodeBerValue(ByteBuffer buf)109   @Override void decodeBerValue(ByteBuffer buf) {
110     value = new BigInteger(getRemaining(buf));
111   }
112 
encodePerImpl(boolean aligned)113   private Iterable<BitStream> encodePerImpl(boolean aligned) {
114     validateValue();
115     if (maximumValue != null && minimumValue != null) {
116       // Encodes a constrained whole numbers according to X.691-0207, 10.5.
117       BigInteger normalizedValue = value.subtract(minimumValue);
118       BigInteger range = maximumValue.subtract(minimumValue);
119       return aligned
120           ? encodeNormalizedIntegerWithRangeAligned(normalizedValue, range)
121           : encodeNormalizedIntegerWithRangeUnaligned(normalizedValue, range);
122     } else if (minimumValue != null) {
123       // Encodes a semi-constrained whole numbers according to X.691-0207, 10.7.
124       return aligned
125           ? PerAlignedUtils.encodeSemiConstrainedLengthOfBytes(
126               PerAlignedUtils.encodeBigNonNegativeWholeNumber(value.subtract(minimumValue)))
127           : PerUnalignedUtils.encodeSemiConstrainedLengthOfBytes(
128               PerUnalignedUtils.encodeBigNonNegativeWholeNumber(value.subtract(minimumValue)));
129     } else {
130       // Encodes an unconstrained whole number according to X.691-0207, 10.8.
131       return aligned
132           ? PerAlignedUtils.encodeUnconstrainedLengthOfBytes(value.toByteArray())
133           : PerUnalignedUtils.encodeSemiConstrainedLengthOfBytes(value.toByteArray());
134     }
135   }
136 
encodePerUnaligned()137   @Override public Iterable<BitStream> encodePerUnaligned() {
138     return encodePerImpl(false);
139   }
140 
encodePerAligned()141   @Override public Iterable<BitStream> encodePerAligned() {
142     return encodePerImpl(true);
143   }
144 
setInteger(BigInteger value)145   public void setInteger(BigInteger value) {
146     this.value = value;
147   }
148 
setInteger(BigInteger value, boolean validateValue)149   public void setInteger(BigInteger value, boolean validateValue) {
150     this.value = value;
151     if (validateValue) {
152       validateValue();
153     }
154   }
155 
getInteger()156   public BigInteger getInteger() {
157     return value;
158   }
159 
decodeNormalizedIntegerWithRangeAligned( BitStreamReader reader, BigInteger range)160   private BigInteger decodeNormalizedIntegerWithRangeAligned(
161       BitStreamReader reader, BigInteger range) {
162     if (range.compareTo(BigInteger.valueOf(SIXTYFOUR_K)) < 0) {
163       int normalizedIntValue = PerAlignedUtils.decodeNormalizedSmallConstrainedWholeNumber(
164               reader, range.intValue());
165       return BigInteger.valueOf(normalizedIntValue);
166     } else {
167       return PerAlignedUtils.decodeBigNonNegativeWholeNumber(
168           PerAlignedUtils.decodeConstrainedLengthOfBytes(
169               reader, 1,
170               PerAlignedUtils.encodeBigNonNegativeWholeNumber(range).length));
171     }
172   }
173 
decodeNormalizedIntegerWithRangeUnaligned( BitStreamReader reader, BigInteger range)174   private BigInteger decodeNormalizedIntegerWithRangeUnaligned(
175       BitStreamReader reader, BigInteger range) {
176     long normalizedIntValue =
177         PerUnalignedUtils.decodeNormalizedConstrainedWholeNumber(
178             reader, range.longValue());
179     return BigInteger.valueOf(normalizedIntValue);
180   }
181 
decodePerImpl(BitStreamReader reader, boolean aligned)182   private void decodePerImpl(BitStreamReader reader, boolean aligned) {
183     if (maximumValue != null && minimumValue != null) {
184       // Decodes a constrained whole numbers according to X.691-0207, 10.5.
185       BigInteger range = maximumValue.subtract(minimumValue);
186       BigInteger normalizedValue = aligned
187           ? decodeNormalizedIntegerWithRangeAligned(reader, range)
188           : decodeNormalizedIntegerWithRangeUnaligned(reader, range);
189       value = minimumValue.add(normalizedValue);
190     } else if (minimumValue != null) {
191       // Decodes a semi-constrained whole numbers according to X.691-0207, 10.7.
192       byte[] intBytes = aligned
193           ? PerAlignedUtils.decodeSemiConstrainedLengthOfBytes(reader)
194           : PerUnalignedUtils.decodeSemiConstrainedLengthOfBytes(reader);
195       value = new BigInteger(convertPositiveToSigned(intBytes)).add(minimumValue);
196     } else {
197       // Decodes an unconstrained whole number according to X.691-0207, 10.8.
198       value = new BigInteger(aligned
199           ? PerAlignedUtils.decodeUnconstrainedLengthOfBytes(reader)
200           : PerUnalignedUtils.decodeSemiConstrainedLengthOfBytes(reader));
201     }
202   }
203 
convertPositiveToSigned(byte[] rawData)204   private byte[] convertPositiveToSigned(byte[] rawData) {
205     if ((rawData[0] & 0x80) != 0) {
206       byte[] data = new byte[rawData.length + 1];
207       System.arraycopy(rawData, 0, data, 1, rawData.length);
208       return data;
209     } else {
210       return rawData;
211     }
212   }
213 
decodePerUnaligned(BitStreamReader reader)214   @Override public void decodePerUnaligned(BitStreamReader reader) {
215     decodePerImpl(reader, false);
216   }
217 
decodePerAligned(BitStreamReader reader)218   @Override public void decodePerAligned(BitStreamReader reader) {
219     decodePerImpl(reader, true);
220   }
221 }
222