View Javadoc
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 }