1 /*
2 * Copyright (c) 1999, 2013, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package davmail.ldap;
27
28 import java.io.UnsupportedEncodingException;
29 import java.nio.charset.StandardCharsets;
30
31 /**
32 * A BER encoder.
33 *
34 * @author Jagane Sundar
35 * @author Scott Seligman
36 * @author Vincent Ryan
37 */
38 public final class BerEncoder extends Ber {
39
40 private int curSeqIndex;
41 private int[] seqOffset;
42 private static final int INITIAL_SEQUENCES = 16;
43 private static final int DEFAULT_BUFSIZE = 1024;
44
45 // When buf is full, expand its size by the following factor.
46 private static final int BUF_GROWTH_FACTOR = 8;
47
48 /**
49 * Creates a BER buffer for encoding.
50 */
51 public BerEncoder() {
52 this(DEFAULT_BUFSIZE);
53 }
54
55 /**
56 * Creates a BER buffer of a specified size for encoding.
57 * Specify the initial bufsize. Buffer will be expanded as needed.
58 * @param bufsize The number of bytes for the buffer.
59 */
60 public BerEncoder(int bufsize) {
61 buf = new byte[bufsize];
62 this.bufsize = bufsize;
63 offset = 0;
64
65 seqOffset = new int[INITIAL_SEQUENCES];
66 curSeqIndex = 0;
67 }
68
69 /**
70 * Resets encoder to state when newly constructed. Zeros out
71 * internal data structures.
72 */
73 public void reset() {
74 while (offset > 0) {
75 buf[--offset] = 0;
76 }
77 while (curSeqIndex > 0) {
78 seqOffset[--curSeqIndex] = 0;
79 }
80 }
81
82 // ------------------ Accessor methods ------------
83
84 /**
85 * Gets the number of encoded bytes in this BER buffer.
86 */
87 public int getDataLen() {
88 return offset;
89 }
90
91 /**
92 * Gets the buffer that contains the BER encoding. Throws an
93 * exception if unmatched beginSeq() and endSeq() pairs were
94 * encountered. Not entire buffer contains encoded bytes.
95 * Use getDataLen() to determine number of encoded bytes.
96 * Use getBuffer(true) to get rid of excess bytes in array.
97 * @throws IllegalStateException If buffer contains unbalanced sequence.
98 */
99 public byte[] getBuf() {
100 if (curSeqIndex != 0) {
101 throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
102 }
103 return buf; // shared buffer, be careful to use this method.
104 }
105
106 /**
107 * Gets the buffer that contains the BER encoding, trimming unused bytes.
108 *
109 * @throws IllegalStateException If buffer contains unbalanced sequence.
110 */
111 @SuppressWarnings("unused")
112 public byte[] getTrimmedBuf() {
113 int len = getDataLen();
114 byte[] trimBuf = new byte[len];
115
116 System.arraycopy(getBuf(), 0, trimBuf, 0, len);
117 return trimBuf;
118 }
119
120 // -------------- encoding methods -------------
121
122 /**
123 * Begin encoding a sequence with a tag.
124 */
125 public void beginSeq(int tag) {
126
127 // Double the size of the SEQUENCE array if it overflows
128 if (curSeqIndex >= seqOffset.length) {
129 int[] seqOffsetTmp = new int[seqOffset.length * 2];
130
131 System.arraycopy(seqOffset, 0, seqOffsetTmp, 0, seqOffset.length);
132 seqOffset = seqOffsetTmp;
133 }
134
135 encodeByte(tag);
136 seqOffset[curSeqIndex] = offset;
137
138 // Save space for sequence length.
139 // %%% Currently we save enough space for sequences up to 64k.
140 // For larger sequences we'll need to shift the data to the right
141 // in endSeq(). If we could instead pad the length field with
142 // zeros, it would be a big win.
143 ensureFreeBytes(3);
144 offset += 3;
145
146 curSeqIndex++;
147 }
148
149 /**
150 * Terminate a BER sequence.
151 */
152 public void endSeq() throws EncodeException {
153 curSeqIndex--;
154 if (curSeqIndex < 0) {
155 throw new IllegalStateException("BER encode error: Unbalanced SEQUENCEs.");
156 }
157
158 int start = seqOffset[curSeqIndex] + 3; // index beyond length field
159 int len = offset - start;
160
161 if (len <= 0x7f) {
162 shiftSeqData(start, len, -2);
163 buf[seqOffset[curSeqIndex]] = (byte) len;
164 } else if (len <= 0xff) {
165 shiftSeqData(start, len, -1);
166 buf[seqOffset[curSeqIndex]] = (byte) 0x81;
167 buf[seqOffset[curSeqIndex] + 1] = (byte) len;
168 } else if (len <= 0xffff) {
169 buf[seqOffset[curSeqIndex]] = (byte) 0x82;
170 buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 8);
171 buf[seqOffset[curSeqIndex] + 2] = (byte) len;
172 } else if (len <= 0xffffff) {
173 shiftSeqData(start, len, 1);
174 buf[seqOffset[curSeqIndex]] = (byte) 0x83;
175 buf[seqOffset[curSeqIndex] + 1] = (byte) (len >> 16);
176 buf[seqOffset[curSeqIndex] + 2] = (byte) (len >> 8);
177 buf[seqOffset[curSeqIndex] + 3] = (byte) len;
178 } else {
179 throw new EncodeException("SEQUENCE too long");
180 }
181 }
182
183 /**
184 * Shifts contents of buf in the range [start,start+len) a specified amount.
185 * Positive shift value means shift to the right.
186 */
187 private void shiftSeqData(int start, int len, int shift) {
188 if (shift > 0) {
189 ensureFreeBytes(shift);
190 }
191 System.arraycopy(buf, start, buf, start + shift, len);
192 offset += shift;
193 }
194
195 /**
196 * Encode a single byte.
197 */
198 public void encodeByte(int b) {
199 ensureFreeBytes(1);
200 buf[offset++] = (byte) b;
201 }
202
203 /*
204 private void deleteByte() {
205 offset--;
206 }
207 */
208
209
210 /*
211 * Encodes an int.
212 *<blockquote><pre>
213 * BER integer ::= 0x02 berlength byte {byte}*
214 *</pre></blockquote>
215 */
216 public void encodeInt(int i) {
217 encodeInt(i, 0x02);
218 }
219
220 /**
221 * Encodes an int and a tag.
222 *<blockquote><pre>
223 * BER integer w tag ::= tag berlength byte {byte}*
224 *</pre></blockquote>
225 */
226 public void encodeInt(int i, int tag) {
227 int mask = 0xff800000;
228 int intsize = 4;
229
230 while( (((i & mask) == 0) || ((i & mask) == mask)) && (intsize > 1) ) {
231 intsize--;
232 i <<= 8;
233 }
234
235 encodeInt(i, tag, intsize);
236 }
237
238 //
239 // encodes an int using numbytes for the actual encoding.
240 //
241 private void encodeInt(int i, int tag, int intsize) {
242
243 //
244 // integer ::= 0x02 asnlength byte {byte}*
245 //
246
247 if (intsize > 4) {
248 throw new IllegalArgumentException("BER encode error: INTEGER too long.");
249 }
250
251 ensureFreeBytes(2 + intsize);
252
253 buf[offset++] = (byte) tag;
254 buf[offset++] = (byte) intsize;
255
256 int mask = 0xff000000;
257
258 while (intsize-- > 0) {
259 buf[offset++] = (byte) ((i & mask) >> 24);
260 i <<= 8;
261 }
262 }
263
264 /**
265 * Encodes a boolean.
266 *<blockquote><pre>
267 * BER boolean ::= 0x01 0x01 {0xff|0x00}
268 *</pre></blockquote>
269 */
270 @SuppressWarnings("unused")
271 public void encodeBoolean(boolean b) {
272 encodeBoolean(b, ASN_BOOLEAN);
273 }
274
275
276 /**
277 * Encodes a boolean and a tag
278 *<blockquote><pre>
279 * BER boolean w TAG ::= tag 0x01 {0xff|0x00}
280 *</pre></blockquote>
281 */
282 public void encodeBoolean(boolean b, int tag) {
283 ensureFreeBytes(3);
284
285 buf[offset++] = (byte) tag;
286 buf[offset++] = 0x01;
287 buf[offset++] = b ? (byte) 0xff : (byte) 0x00;
288 }
289
290 /**
291 * Encodes a string.
292 *<blockquote><pre>
293 * BER string ::= 0x04 strlen byte1 byte2...
294 *</pre></blockquote>
295 * The string is converted into bytes using UTF-8 or ISO-Latin-1.
296 */
297 public void encodeString(String str, boolean encodeUTF8)
298 throws EncodeException {
299 encodeString(str, ASN_OCTET_STR, encodeUTF8);
300 }
301
302 /**
303 * Encodes a string and a tag.
304 *<blockquote><pre>
305 * BER string w TAG ::= tag strlen byte1 byte2...
306 *</pre></blockquote>
307 */
308 public void encodeString(String str, int tag, boolean encodeUTF8)
309 throws EncodeException {
310
311 encodeByte(tag);
312
313 int i = 0;
314 int count;
315 byte[] bytes = null;
316
317 if (str == null) {
318 count = 0;
319 } else if (encodeUTF8) {
320 bytes = str.getBytes(StandardCharsets.UTF_8);
321 count = bytes.length;
322 } else {
323 try {
324 bytes = str.getBytes("8859_1");
325 count = bytes.length;
326 } catch (UnsupportedEncodingException e) {
327 throw new EncodeException("8859_1 not available on platform");
328 }
329 }
330
331 encodeLength(count);
332
333 ensureFreeBytes(count);
334 while (i < count) {
335 buf[offset++] = bytes[i++];
336 }
337 }
338
339 /**
340 * Encodes a portion of an octet string and a tag.
341 */
342 public void encodeOctetString(byte[] tb, int tag, int tboffset, int length)
343 throws EncodeException {
344
345 encodeByte(tag);
346 encodeLength(length);
347
348 if (length > 0) {
349 ensureFreeBytes(length);
350 System.arraycopy(tb, tboffset, buf, offset, length);
351 offset += length;
352 }
353 }
354
355 /**
356 * Encodes an octet string and a tag.
357 */
358 public void encodeOctetString(byte[] tb, int tag) throws EncodeException {
359 encodeOctetString(tb, tag, 0, tb.length);
360 }
361
362 private void encodeLength(int len) throws EncodeException {
363 ensureFreeBytes(4); // worst case
364
365 if (len < 128) {
366 buf[offset++] = (byte) len;
367 } else if (len <= 0xff) {
368 buf[offset++] = (byte) 0x81;
369 buf[offset++] = (byte) len;
370 } else if (len <= 0xffff) {
371 buf[offset++] = (byte) 0x82;
372 buf[offset++] = (byte) (len >> 8);
373 buf[offset++] = (byte) (len & 0xff);
374 } else if (len <= 0xffffff) {
375 buf[offset++] = (byte) 0x83;
376 buf[offset++] = (byte) (len >> 16);
377 buf[offset++] = (byte) (len >> 8);
378 buf[offset++] = (byte) (len & 0xff);
379 } else {
380 throw new EncodeException("string too long");
381 }
382 }
383
384 /**
385 * Encodes an array of strings.
386 */
387 @SuppressWarnings("unused")
388 public void encodeStringArray(String[] strs, boolean encodeUTF8)
389 throws EncodeException {
390 if (strs == null)
391 return;
392 for (String str : strs) {
393 encodeString(str, encodeUTF8);
394 }
395 }
396 /*
397 private void encodeNull() {
398
399 //
400 // NULL ::= 0x05 0x00
401 //
402 encodeByte(0x05);
403 encodeByte(0x00);
404 }
405 */
406
407 /**
408 * Ensures that there are at least "len" unused bytes in "buf".
409 * When more space is needed "buf" is expanded by a factor of
410 * BUF_GROWTH_FACTOR, then "len" bytes are added if "buf" still
411 * isn't large enough.
412 */
413 private void ensureFreeBytes(int len) {
414 if (bufsize - offset < len) {
415 int newsize = bufsize * BUF_GROWTH_FACTOR;
416 if (newsize - offset < len) {
417 newsize += len;
418 }
419 byte[] newbuf = new byte[newsize];
420 // Only copy bytes in the range [0, offset)
421 System.arraycopy(buf, 0, newbuf, 0, offset);
422
423 buf = newbuf;
424 bufsize = newsize;
425 }
426 }
427 }