1//
2//  ========================================================================
3//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
4//  ------------------------------------------------------------------------
5//  All rights reserved. This program and the accompanying materials
6//  are made available under the terms of the Eclipse Public License v1.0
7//  and Apache License v2.0 which accompanies this distribution.
8//
9//      The Eclipse Public License is available at
10//      http://www.eclipse.org/legal/epl-v10.html
11//
12//      The Apache License v2.0 is available at
13//      http://www.opensource.org/licenses/apache2.0.php
14//
15//  You may elect to redistribute this code under either of these licenses.
16//  ========================================================================
17//
18
19package org.eclipse.jetty.util;
20
21import java.io.ByteArrayOutputStream;
22import java.io.IOException;
23import java.io.UnsupportedEncodingException;
24
25
26/* ------------------------------------------------------------ */
27/** Fast B64 Encoder/Decoder as described in RFC 1421.
28 * <p>Does not insert or interpret whitespace as described in RFC
29 * 1521. If you require this you must pre/post process your data.
30 * <p> Note that in a web context the usual case is to not want
31 * linebreaks or other white space in the encoded output.
32 *
33 */
34public class B64Code
35{
36    // ------------------------------------------------------------------
37    static final char __pad='=';
38    static final char[] __rfc1421alphabet=
39            {
40                'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P',
41                'Q','R','S','T','U','V','W','X','Y','Z','a','b','c','d','e','f',
42                'g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v',
43                'w','x','y','z','0','1','2','3','4','5','6','7','8','9','+','/'
44            };
45
46    static final byte[] __rfc1421nibbles;
47
48    static
49    {
50        __rfc1421nibbles=new byte[256];
51        for (int i=0;i<256;i++)
52            __rfc1421nibbles[i]=-1;
53        for (byte b=0;b<64;b++)
54            __rfc1421nibbles[(byte)__rfc1421alphabet[b]]=b;
55        __rfc1421nibbles[(byte)__pad]=0;
56    }
57
58    // ------------------------------------------------------------------
59    /**
60     * Base 64 encode as described in RFC 1421.
61     * <p>Does not insert whitespace as described in RFC 1521.
62     * @param s String to encode.
63     * @return String containing the encoded form of the input.
64     */
65    static public String encode(String s)
66    {
67        try
68        {
69            return encode(s,null);
70        }
71        catch (UnsupportedEncodingException e)
72        {
73            throw new IllegalArgumentException(e.toString());
74        }
75    }
76
77    // ------------------------------------------------------------------
78    /**
79     * Base 64 encode as described in RFC 1421.
80     * <p>Does not insert whitespace as described in RFC 1521.
81     * @param s String to encode.
82     * @param charEncoding String representing the name of
83     *        the character encoding of the provided input String.
84     * @return String containing the encoded form of the input.
85     */
86    static public String encode(String s,String charEncoding)
87            throws UnsupportedEncodingException
88    {
89        byte[] bytes;
90        if (charEncoding==null)
91            bytes=s.getBytes(StringUtil.__ISO_8859_1);
92        else
93            bytes=s.getBytes(charEncoding);
94
95        return new String(encode(bytes));
96    }
97
98    // ------------------------------------------------------------------
99    /**
100     * Fast Base 64 encode as described in RFC 1421.
101     * <p>Does not insert whitespace as described in RFC 1521.
102     * <p> Avoids creating extra copies of the input/output.
103     * @param b byte array to encode.
104     * @return char array containing the encoded form of the input.
105     */
106    static public char[] encode(byte[] b)
107    {
108        if (b==null)
109            return null;
110
111        int bLen=b.length;
112        int cLen=((bLen+2)/3)*4;
113        char c[]=new char[cLen];
114        int ci=0;
115        int bi=0;
116        byte b0, b1, b2;
117        int stop=(bLen/3)*3;
118        while (bi<stop)
119        {
120            b0=b[bi++];
121            b1=b[bi++];
122            b2=b[bi++];
123            c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
124            c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
125            c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
126            c[ci++]=__rfc1421alphabet[b2&077];
127        }
128
129        if (bLen!=bi)
130        {
131            switch (bLen%3)
132            {
133                case 2:
134                    b0=b[bi++];
135                    b1=b[bi++];
136                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
137                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
138                    c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
139                    c[ci++]=__pad;
140                    break;
141
142                case 1:
143                    b0=b[bi++];
144                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
145                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
146                    c[ci++]=__pad;
147                    c[ci++]=__pad;
148                    break;
149
150                default:
151                    break;
152            }
153        }
154
155        return c;
156    }
157
158    // ------------------------------------------------------------------
159    /**
160     * Fast Base 64 encode as described in RFC 1421 and RFC2045
161     * <p>Does not insert whitespace as described in RFC 1521, unless rfc2045 is passed as true.
162     * <p> Avoids creating extra copies of the input/output.
163     * @param b byte array to encode.
164     * @param rfc2045 If true, break lines at 76 characters with CRLF
165     * @return char array containing the encoded form of the input.
166     */
167    static public char[] encode(byte[] b, boolean rfc2045)
168    {
169        if (b==null)
170            return null;
171        if (!rfc2045)
172            return encode(b);
173
174        int bLen=b.length;
175        int cLen=((bLen+2)/3)*4;
176        cLen+=2+2*(cLen/76);
177        char c[]=new char[cLen];
178        int ci=0;
179        int bi=0;
180        byte b0, b1, b2;
181        int stop=(bLen/3)*3;
182        int l=0;
183        while (bi<stop)
184        {
185            b0=b[bi++];
186            b1=b[bi++];
187            b2=b[bi++];
188            c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
189            c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
190            c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f|(b2>>>6)&0x03];
191            c[ci++]=__rfc1421alphabet[b2&077];
192            l+=4;
193            if (l%76==0)
194            {
195                c[ci++]=13;
196                c[ci++]=10;
197            }
198        }
199
200        if (bLen!=bi)
201        {
202            switch (bLen%3)
203            {
204                case 2:
205                    b0=b[bi++];
206                    b1=b[bi++];
207                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
208                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f|(b1>>>4)&0x0f];
209                    c[ci++]=__rfc1421alphabet[(b1<<2)&0x3f];
210                    c[ci++]=__pad;
211                    break;
212
213                case 1:
214                    b0=b[bi++];
215                    c[ci++]=__rfc1421alphabet[(b0>>>2)&0x3f];
216                    c[ci++]=__rfc1421alphabet[(b0<<4)&0x3f];
217                    c[ci++]=__pad;
218                    c[ci++]=__pad;
219                    break;
220
221                default:
222                    break;
223            }
224        }
225
226        c[ci++]=13;
227        c[ci++]=10;
228        return c;
229    }
230
231    // ------------------------------------------------------------------
232    /**
233     * Base 64 decode as described in RFC 2045.
234     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
235     * @param encoded String to decode.
236     * @param charEncoding String representing the character encoding
237     *        used to map the decoded bytes into a String.
238     * @return String decoded byte array.
239     * @throws UnsupportedEncodingException if the encoding is not supported
240     * @throws IllegalArgumentException if the input is not a valid
241     *         B64 encoding.
242     */
243    static public String decode(String encoded,String charEncoding)
244            throws UnsupportedEncodingException
245    {
246        byte[] decoded=decode(encoded);
247        if (charEncoding==null)
248            return new String(decoded);
249        return new String(decoded,charEncoding);
250    }
251
252    /* ------------------------------------------------------------ */
253    /**
254     * Fast Base 64 decode as described in RFC 1421.
255     *
256     * <p>Unlike other decode methods, this does not attempt to
257     * cope with extra whitespace as described in RFC 1521/2045.
258     * <p> Avoids creating extra copies of the input/output.
259     * <p> Note this code has been flattened for performance.
260     * @param b char array to decode.
261     * @return byte array containing the decoded form of the input.
262     * @throws IllegalArgumentException if the input is not a valid
263     *         B64 encoding.
264     */
265    static public byte[] decode(char[] b)
266    {
267        if (b==null)
268            return null;
269
270        int bLen=b.length;
271        if (bLen%4!=0)
272            throw new IllegalArgumentException("Input block size is not 4");
273
274        int li=bLen-1;
275        while (li>=0 && b[li]==(byte)__pad)
276            li--;
277
278        if (li<0)
279            return new byte[0];
280
281        // Create result array of exact required size.
282        int rLen=((li+1)*3)/4;
283        byte r[]=new byte[rLen];
284        int ri=0;
285        int bi=0;
286        int stop=(rLen/3)*3;
287        byte b0,b1,b2,b3;
288        try
289        {
290            while (ri<stop)
291            {
292                b0=__rfc1421nibbles[b[bi++]];
293                b1=__rfc1421nibbles[b[bi++]];
294                b2=__rfc1421nibbles[b[bi++]];
295                b3=__rfc1421nibbles[b[bi++]];
296                if (b0<0 || b1<0 || b2<0 || b3<0)
297                    throw new IllegalArgumentException("Not B64 encoded");
298
299                r[ri++]=(byte)(b0<<2|b1>>>4);
300                r[ri++]=(byte)(b1<<4|b2>>>2);
301                r[ri++]=(byte)(b2<<6|b3);
302            }
303
304            if (rLen!=ri)
305            {
306                switch (rLen%3)
307                {
308                    case 2:
309                        b0=__rfc1421nibbles[b[bi++]];
310                        b1=__rfc1421nibbles[b[bi++]];
311                        b2=__rfc1421nibbles[b[bi++]];
312                        if (b0<0 || b1<0 || b2<0)
313                            throw new IllegalArgumentException("Not B64 encoded");
314                        r[ri++]=(byte)(b0<<2|b1>>>4);
315                        r[ri++]=(byte)(b1<<4|b2>>>2);
316                        break;
317
318                    case 1:
319                        b0=__rfc1421nibbles[b[bi++]];
320                        b1=__rfc1421nibbles[b[bi++]];
321                        if (b0<0 || b1<0)
322                            throw new IllegalArgumentException("Not B64 encoded");
323                        r[ri++]=(byte)(b0<<2|b1>>>4);
324                        break;
325
326                    default:
327                        break;
328                }
329            }
330        }
331        catch (IndexOutOfBoundsException e)
332        {
333            throw new IllegalArgumentException("char "+bi
334                    +" was not B64 encoded");
335        }
336
337        return r;
338    }
339
340    /* ------------------------------------------------------------ */
341    /**
342     * Base 64 decode as described in RFC 2045.
343     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
344     * @param encoded String to decode.
345     * @return byte array containing the decoded form of the input.
346     * @throws IllegalArgumentException if the input is not a valid
347     *         B64 encoding.
348     */
349    static public byte[] decode(String encoded)
350    {
351        if (encoded==null)
352            return null;
353
354        ByteArrayOutputStream bout = new ByteArrayOutputStream(4*encoded.length()/3);
355        decode(encoded, bout);
356        return bout.toByteArray();
357    }
358
359    /* ------------------------------------------------------------ */
360    /**
361     * Base 64 decode as described in RFC 2045.
362     * <p>Unlike {@link #decode(char[])}, extra whitespace is ignored.
363     * @param encoded String to decode.
364     * @param output stream for decoded bytes
365     * @return byte array containing the decoded form of the input.
366     * @throws IllegalArgumentException if the input is not a valid
367     *         B64 encoding.
368     */
369    static public void decode (String encoded, ByteArrayOutputStream bout)
370    {
371        if (encoded==null)
372            return;
373
374        if (bout == null)
375            throw new IllegalArgumentException("No outputstream for decoded bytes");
376
377        int ci=0;
378        byte nibbles[] = new byte[4];
379        int s=0;
380
381        while (ci<encoded.length())
382        {
383            char c=encoded.charAt(ci++);
384
385            if (c==__pad)
386                break;
387
388            if (Character.isWhitespace(c))
389                continue;
390
391            byte nibble=__rfc1421nibbles[c];
392            if (nibble<0)
393                throw new IllegalArgumentException("Not B64 encoded");
394
395            nibbles[s++]=__rfc1421nibbles[c];
396
397            switch(s)
398            {
399                case 1:
400                    break;
401                case 2:
402                    bout.write(nibbles[0]<<2|nibbles[1]>>>4);
403                    break;
404                case 3:
405                    bout.write(nibbles[1]<<4|nibbles[2]>>>2);
406                    break;
407                case 4:
408                    bout.write(nibbles[2]<<6|nibbles[3]);
409                    s=0;
410                    break;
411            }
412
413        }
414
415        return;
416    }
417
418
419    /* ------------------------------------------------------------ */
420    public static void encode(int value,Appendable buf) throws IOException
421    {
422        buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
423        buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
424        buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
425        buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
426        buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
427        buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4)]);
428        buf.append('=');
429    }
430
431    /* ------------------------------------------------------------ */
432    public static void encode(long lvalue,Appendable buf) throws IOException
433    {
434        int value=(int)(0xFFFFFFFC&(lvalue>>32));
435        buf.append(__rfc1421alphabet[0x3f&((0xFC000000&value)>>26)]);
436        buf.append(__rfc1421alphabet[0x3f&((0x03F00000&value)>>20)]);
437        buf.append(__rfc1421alphabet[0x3f&((0x000FC000&value)>>14)]);
438        buf.append(__rfc1421alphabet[0x3f&((0x00003F00&value)>>8)]);
439        buf.append(__rfc1421alphabet[0x3f&((0x000000FC&value)>>2)]);
440
441        buf.append(__rfc1421alphabet[0x3f&((0x00000003&value)<<4) + (0xf&(int)(lvalue>>28))]);
442
443        value=0x0FFFFFFF&(int)lvalue;
444        buf.append(__rfc1421alphabet[0x3f&((0x0FC00000&value)>>22)]);
445        buf.append(__rfc1421alphabet[0x3f&((0x003F0000&value)>>16)]);
446        buf.append(__rfc1421alphabet[0x3f&((0x0000FC00&value)>>10)]);
447        buf.append(__rfc1421alphabet[0x3f&((0x000003F0&value)>>4)]);
448        buf.append(__rfc1421alphabet[0x3f&((0x0000000F&value)<<2)]);
449    }
450}
451