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