001 /*
002 * CDDL HEADER START
003 *
004 * The contents of this file are subject to the terms of the
005 * Common Development and Distribution License, Version 1.0 only
006 * (the "License"). You may not use this file except in compliance
007 * with the License.
008 *
009 * You can obtain a copy of the license at
010 * trunk/opends/resource/legal-notices/OpenDS.LICENSE
011 * or https://OpenDS.dev.java.net/OpenDS.LICENSE.
012 * See the License for the specific language governing permissions
013 * and limitations under the License.
014 *
015 * When distributing Covered Code, include this CDDL HEADER in each
016 * file and include the License file at
017 * trunk/opends/resource/legal-notices/OpenDS.LICENSE. If applicable,
018 * add the following below this CDDL HEADER, with the fields enclosed
019 * by brackets "[]" replaced with your own identifying information:
020 * Portions Copyright [yyyy] [name of copyright owner]
021 *
022 * CDDL HEADER END
023 *
024 *
025 * Copyright 2006-2008 Sun Microsystems, Inc.
026 */
027 package org.opends.server.protocols.asn1;
028 import org.opends.messages.Message;
029
030
031
032 import java.util.ArrayList;
033 import java.util.Iterator;
034
035 import static org.opends.messages.ProtocolMessages.*;
036 import static org.opends.server.protocols.asn1.ASN1Constants.*;
037 import static org.opends.server.util.ServerConstants.*;
038 import static org.opends.server.util.StaticUtils.*;
039
040
041
042 /**
043 * This class defines the data structures and methods to use when interacting
044 * with ASN.1 sequence elements.
045 */
046 @org.opends.server.types.PublicAPI(
047 stability=org.opends.server.types.StabilityLevel.UNCOMMITTED,
048 mayInstantiate=true,
049 mayExtend=false,
050 mayInvoke=true)
051 public final class ASN1Sequence
052 extends ASN1Element
053 {
054 /**
055 * The serial version identifier required to satisfy the compiler because this
056 * class implements the <CODE>java.io.Serializable</CODE> interface. This
057 * value was generated using the <CODE>serialver</CODE> command-line utility
058 * included with the Java SDK.
059 */
060 private static final long serialVersionUID = 694649828357992307L;
061
062
063
064 // The set of ASN.1 elements contained in this sequence.
065 private ArrayList<ASN1Element> elements;
066
067
068
069
070 /**
071 * Creates a new ASN.1 sequence element with the default type no value.
072 */
073 public ASN1Sequence()
074 {
075 super(UNIVERSAL_SEQUENCE_TYPE);
076
077
078 this.elements = new ArrayList<ASN1Element>();
079 }
080
081
082
083 /**
084 * Creates a new ASN.1 sequence element with the specified type and no value.
085 *
086 * @param type The BER type for this ASN.1 sequence.
087 */
088 public ASN1Sequence(byte type)
089 {
090 super(type);
091
092
093 this.elements = new ArrayList<ASN1Element>();
094 }
095
096
097
098 /**
099 * Creates a new ASN.1 sequence with the default type and the provided set of
100 * elements.
101 *
102 * @param elements The set of elements to include in this sequence.
103 */
104 public ASN1Sequence(ArrayList<ASN1Element> elements)
105 {
106 super(UNIVERSAL_SEQUENCE_TYPE, encodeValue(elements));
107
108
109 if (elements == null)
110 {
111 this.elements = new ArrayList<ASN1Element>();
112 }
113 else
114 {
115 this.elements = elements;
116 }
117 }
118
119
120
121 /**
122 * Creates a new ASN.1 sequence with the specified type and the provided set
123 * of elements.
124 *
125 * @param type The BER type for this sequence.
126 * @param elements The set of elements to include in this sequence.
127 */
128 public ASN1Sequence(byte type, ArrayList<ASN1Element> elements)
129 {
130 super(type, encodeValue(elements));
131
132
133 if (elements == null)
134 {
135 this.elements = new ArrayList<ASN1Element>();
136 }
137 else
138 {
139 this.elements = elements;
140 }
141 }
142
143
144
145 /**
146 * Creates a new ASN.1 sequence with the specified type and value and the
147 * provided set of elements.
148 *
149 * @param type The BER type for this sequence.
150 * @param value The encoded value for this sequence.
151 * @param elements The set of elements to include in this sequence.
152 */
153 private ASN1Sequence(byte type, byte[] value, ArrayList<ASN1Element> elements)
154 {
155 super(type, value);
156
157
158 if (elements == null)
159 {
160 this.elements = new ArrayList<ASN1Element>();
161 }
162 else
163 {
164 this.elements = elements;
165 }
166 }
167
168
169
170 /**
171 * Retrieves the set of elements contained in this ASN.1 sequence. The
172 * returned list must not be modified by the caller.
173 *
174 * @return The set of elements contained in this ASN.1 sequence.
175 */
176 public ArrayList<ASN1Element> elements()
177 {
178 return elements;
179 }
180
181
182
183 /**
184 * Specifies the set of elements for this ASN.1 sequence.
185 *
186 * @param elements The set of elements for this ASN.1 sequence.
187 */
188 public void setElements(ArrayList<ASN1Element> elements)
189 {
190 if (elements == null)
191 {
192 this.elements.clear();
193 setValueInternal(NO_VALUE);
194 }
195 else
196 {
197 this.elements = elements;
198 setValueInternal(encodeValue(elements));
199 }
200 }
201
202
203
204 /**
205 * Specifies the value for this ASN.1 sequence element.
206 *
207 * @param value The encoded value for this ASN.1 sequence element.
208 *
209 * @throws ASN1Exception If the provided array is null or cannot be decoded
210 * as a set of ASN.1 elements.
211 */
212 public void setValue(byte[] value)
213 throws ASN1Exception
214 {
215 if (value == null)
216 {
217 Message message = ERR_ASN1_SEQUENCE_SET_VALUE_NULL.get();
218 throw new ASN1Exception(message);
219 }
220
221 elements = decodeElements(value);
222 setValueInternal(value);
223 }
224
225
226
227 /**
228 * Decodes the provided ASN.1 element as a sequence element.
229 *
230 * @param element The ASN.1 element to decode as a sequence element.
231 *
232 * @return The decoded ASN.1 sequence element.
233 *
234 * @throws ASN1Exception If the provided ASN.1 element cannot be decoded as
235 * a sequence element.
236 */
237 public static ASN1Sequence decodeAsSequence(ASN1Element element)
238 throws ASN1Exception
239 {
240 if (element == null)
241 {
242 Message message = ERR_ASN1_SEQUENCE_DECODE_ELEMENT_NULL.get();
243 throw new ASN1Exception(message);
244 }
245
246 byte[] value = element.value();
247 ArrayList<ASN1Element> elements = decodeElements(value);
248 return new ASN1Sequence(element.getType(), value, elements);
249 }
250
251
252
253 /**
254 * Decodes the provided byte array as an ASN.1 sequence element.
255 *
256 * @param encodedElement The byte array to decode as an ASN.1 sequence
257 * element.
258 *
259 * @return The decoded ASN.1 sequence element.
260 *
261 * @throws ASN1Exception If the provided byte array cannot be decoded as an
262 * ASN.1 sequence element.
263 */
264 public static ASN1Sequence decodeAsSequence(byte[] encodedElement)
265 throws ASN1Exception
266 {
267 // First make sure that the array is not null and long enough to contain
268 // a valid ASN.1 sequence element.
269 if (encodedElement == null)
270 {
271 Message message = ERR_ASN1_SEQUENCE_DECODE_ARRAY_NULL.get();
272 throw new ASN1Exception(message);
273 }
274
275 if (encodedElement.length < 2)
276 {
277 Message message = ERR_ASN1_SHORT_ELEMENT.get(encodedElement.length);
278 throw new ASN1Exception(message);
279 }
280
281
282 // Next, decode the length. This allows multi-byte lengths with up to four
283 // bytes used to indicate how many bytes are in the length.
284 byte type = encodedElement[0];
285 int length = (encodedElement[1] & 0x7F);
286 int valueStartPos = 2;
287 if (length != encodedElement[1])
288 {
289 int numLengthBytes = length;
290 if (numLengthBytes > 4)
291 {
292 Message message = ERR_ASN1_INVALID_NUM_LENGTH_BYTES.get(numLengthBytes);
293 throw new ASN1Exception(message);
294 }
295 else if (encodedElement.length < (2 + numLengthBytes))
296 {
297 Message message = ERR_ASN1_TRUNCATED_LENGTH.get(numLengthBytes);
298 throw new ASN1Exception(message);
299 }
300
301 length = 0x00;
302 valueStartPos = 2 + numLengthBytes;
303 for (int i=0; i < numLengthBytes; i++)
304 {
305 length = (length << 8) | (encodedElement[i+2] & 0xFF);
306 }
307 }
308
309
310 // Make sure that the number of bytes left is equal to the number of bytes
311 // in the value.
312 if ((encodedElement.length - valueStartPos) != length)
313 {
314 Message message = ERR_ASN1_LENGTH_MISMATCH.get(
315 length, (encodedElement.length - valueStartPos));
316 throw new ASN1Exception(message);
317 }
318
319
320 // Copy the value, decode the elements it contains, and construct the
321 // sequence to return.
322 byte[] value = new byte[length];
323 System.arraycopy(encodedElement, valueStartPos, value, 0, length);
324 ArrayList<ASN1Element> elements = decodeElements(value);
325 return new ASN1Sequence(type, value, elements);
326 }
327
328
329
330 /**
331 * Decodes the provided information as an ASN.1 sequence.
332 *
333 * @param type The BER type to use for the sequence.
334 * @param encodedValue The encoded value to decode as the set of elements
335 * for the sequence.
336 *
337 * @return The decoded ASN.1 sequence element.
338 *
339 * @throws ASN1Exception If the provided byte array cannot be decoded as an
340 * ASN.1 sequence element.
341 */
342 public static ASN1Sequence decodeAsSequence(byte type, byte[] encodedValue)
343 throws ASN1Exception
344 {
345 ArrayList<ASN1Element> elements = decodeElements(encodedValue);
346 return new ASN1Sequence(type, encodedValue, elements);
347 }
348
349
350
351 /**
352 * Appends a string representation of this ASN.1 sequence element to the
353 * provided buffer.
354 *
355 * @param buffer The buffer to which the information should be appended.
356 */
357 public void toString(StringBuilder buffer)
358 {
359 buffer.append("ASN1Sequence(type=");
360 buffer.append(byteToHex(getType()));
361 buffer.append(", values={ ");
362
363 if (! elements.isEmpty())
364 {
365 Iterator<ASN1Element> iterator = elements.iterator();
366
367 iterator.next().toString(buffer);
368
369 while (iterator.hasNext())
370 {
371 buffer.append(", ");
372 iterator.next().toString(buffer);
373 }
374 }
375
376 buffer.append(" })");
377 }
378
379
380
381 /**
382 * Appends a string representation of this protocol element to the provided
383 * buffer.
384 *
385 * @param buffer The buffer into which the string representation should be
386 * written.
387 * @param indent The number of spaces that should be used to indent the
388 * resulting string representation.
389 */
390 public void toString(StringBuilder buffer, int indent)
391 {
392 StringBuilder indentBuf = new StringBuilder(indent);
393 for (int i=0 ; i < indent; i++)
394 {
395 indentBuf.append(' ');
396 }
397
398 buffer.append(indentBuf);
399 buffer.append("ASN.1 Sequence");
400 buffer.append(EOL);
401
402 buffer.append(indentBuf);
403 buffer.append(" BER Type: ");
404 buffer.append(byteToHex(getType()));
405 buffer.append(EOL);
406
407 if (! elements.isEmpty())
408 {
409 buffer.append(indentBuf);
410 buffer.append(" Decoded Values:");
411 buffer.append(EOL);
412
413 Iterator<ASN1Element> iterator = elements.iterator();
414
415 buffer.append(indentBuf);
416 buffer.append(" ");
417 iterator.next().toString(buffer);
418 buffer.append(EOL);
419
420 while (iterator.hasNext())
421 {
422 buffer.append(indentBuf);
423 buffer.append(" ");
424 iterator.next().toString(buffer);
425 buffer.append(EOL);
426 }
427 }
428
429
430 buffer.append(indentBuf);
431 buffer.append(" Value: ");
432 buffer.append(EOL);
433 byteArrayToHexPlusAscii(buffer, value(), indent+2);
434 }
435 }
436