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.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 }