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 import com.google.common.io.BaseEncoding;
24 
25 import java.nio.ByteBuffer;
26 import java.util.Collection;
27 import java.util.Objects;
28 
29 /**
30  * Implements ASN.1 functionality.
31  *
32  */
33 public class Asn1OctetString extends Asn1Object {
34   private static final BaseEncoding HEX = BaseEncoding.base16();
35   private static final Collection<Asn1Tag> possibleFirstTags =
36       ImmutableList.of(Asn1Tag.OCTET_STRING);
37 
38   private int minimumSize = 0;
39   private Integer maximumSize = null; // null == unbounded.
40   private byte[] value;
41 
getPossibleFirstTags()42   public static Collection<Asn1Tag> getPossibleFirstTags() {
43     return possibleFirstTags;
44   }
45 
setMinSize(int min)46   protected void setMinSize(int min) {
47     minimumSize = min;
48   }
49 
setMaxSize(int max)50   protected void setMaxSize(int max) {
51     maximumSize = max;
52   }
53 
getValue()54   public byte[] getValue() {
55     return value;
56   }
57 
setValue(byte[] value)58   public void setValue(byte[] value) {
59     this.value = value;
60   }
61 
getDefaultTag()62   @Override Asn1Tag getDefaultTag() {
63     return Asn1Tag.OCTET_STRING;
64   }
65 
getBerValueLength()66   @Override int getBerValueLength() {
67     Objects.requireNonNull(value, "No value set.");
68     return value.length;
69   }
70 
encodeBerValue(ByteBuffer buf)71   @Override void encodeBerValue(ByteBuffer buf) {
72     Objects.requireNonNull(value, "No value set.");
73     buf.put(value);
74   }
75 
decodeBerValue(ByteBuffer buf)76   @Override void decodeBerValue(ByteBuffer buf) {
77     value = getRemaining(buf);
78   }
79 
encodePerImpl(boolean aligned)80   private Iterable<BitStream> encodePerImpl(boolean aligned) {
81     Objects.requireNonNull(value, "No value set.");
82     Preconditions.checkState(
83         maximumSize == null || value.length <= maximumSize, "Too large %s",
84         value.length);
85     if (maximumSize == null) {
86       if (aligned) {
87         return PerAlignedUtils.encodeSemiConstrainedLengthOfBytes(value);
88       } else {
89         return PerUnalignedUtils.encodeSemiConstrainedLengthOfBytes(value);
90       }
91     } else if (minimumSize == maximumSize) {
92       if (maximumSize == 0) {
93         return ImmutableList.of();
94       }
95       if (maximumSize < SIXTYFOUR_K) {
96         BitStream result = new BitStream();
97         for (int i = 0; i < maximumSize; i++) {
98           result.appendByte(value[i]);
99         }
100         if (aligned && maximumSize > 2) {
101           result.setBeginByteAligned();
102         }
103         return ImmutableList.of(result);
104       }
105     }
106     if (aligned) {
107       return PerAlignedUtils.encodeConstrainedLengthOfBytes(
108           value, minimumSize, maximumSize);
109     } else {
110       return PerUnalignedUtils.encodeConstrainedLengthOfBytes(
111           value, minimumSize, maximumSize);
112     }
113   }
114 
encodePerUnaligned()115   @Override public Iterable<BitStream> encodePerUnaligned() {
116     return encodePerImpl(false);
117   }
118 
encodePerAligned()119   @Override public Iterable<BitStream> encodePerAligned() {
120     return encodePerImpl(true);
121   }
122 
decodePerImpl(BitStreamReader reader, boolean aligned)123   private void decodePerImpl(BitStreamReader reader, boolean aligned) {
124     if (maximumSize == null) {
125       if (aligned) {
126         value = PerAlignedUtils.decodeSemiConstrainedLengthOfBytes(reader);
127       } else {
128         value = PerUnalignedUtils.decodeSemiConstrainedLengthOfBytes(reader);
129       }
130       return;
131     } else if (minimumSize == maximumSize) {
132       value = new byte[maximumSize];
133       if (maximumSize == 0) {
134         return;
135       }
136       if (maximumSize < SIXTYFOUR_K) {
137         if (aligned && maximumSize > 2) {
138           reader.spoolToByteBoundary();
139         }
140         for (int i = 0; i < maximumSize; i++) {
141           value[i] = reader.readByte();
142         }
143         return;
144       }
145     }
146     if (aligned) {
147       value = PerAlignedUtils.decodeConstrainedLengthOfBytes(
148           reader, minimumSize, maximumSize);
149     } else {
150       value = PerUnalignedUtils.decodeConstrainedLengthOfBytes(
151           reader, minimumSize, maximumSize);
152     }
153   }
154 
decodePerUnaligned(BitStreamReader reader)155   @Override public void decodePerUnaligned(BitStreamReader reader) {
156     decodePerImpl(reader, false);
157   }
158 
decodePerAligned(BitStreamReader reader)159   @Override public void decodePerAligned(BitStreamReader reader) {
160     decodePerImpl(reader, true);
161   }
162 
toString()163   @Override public String toString() {
164     return toIndentedString("");
165   }
166 
toIndentedString(String indent)167   public String toIndentedString(String indent) {
168     return getTypeName() + " = [ " + (value == null ? "<null>" : HEX.encode(value)) + " ];\n";
169   }
170 
getTypeName()171   protected String getTypeName() {
172     return "";
173   }
174 }
175