Shuyi Chen | d7955ce | 2013-05-22 14:51:55 -0700 | [diff] [blame] | 1 | // Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org) |
| 2 | |
| 3 | package org.xbill.DNS.utils; |
| 4 | |
| 5 | import java.io.*; |
| 6 | |
| 7 | /** |
| 8 | * Routines for converting between Strings of base32-encoded data and arrays |
| 9 | * of binary data. This currently supports the base32 and base32hex alphabets |
| 10 | * specified in RFC 4648, sections 6 and 7. |
| 11 | * |
| 12 | * @author Brian Wellington |
| 13 | */ |
| 14 | |
| 15 | public class base32 { |
| 16 | |
| 17 | public static class Alphabet { |
| 18 | private Alphabet() {} |
| 19 | |
| 20 | public static final String BASE32 = |
| 21 | "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567="; |
| 22 | public static final String BASE32HEX = |
| 23 | "0123456789ABCDEFGHIJKLMNOPQRSTUV="; |
| 24 | }; |
| 25 | |
| 26 | private String alphabet; |
| 27 | private boolean padding, lowercase; |
| 28 | |
| 29 | /** |
| 30 | * Creates an object that can be used to do base32 conversions. |
| 31 | * @param alphabet Which alphabet should be used |
| 32 | * @param padding Whether padding should be used |
| 33 | * @param lowercase Whether lowercase characters should be used. |
| 34 | * default parameters (The standard base32 alphabet, no padding, uppercase) |
| 35 | */ |
| 36 | public |
| 37 | base32(String alphabet, boolean padding, boolean lowercase) { |
| 38 | this.alphabet = alphabet; |
| 39 | this.padding = padding; |
| 40 | this.lowercase = lowercase; |
| 41 | } |
| 42 | |
| 43 | static private int |
| 44 | blockLenToPadding(int blocklen) { |
| 45 | switch (blocklen) { |
| 46 | case 1: |
| 47 | return 6; |
| 48 | case 2: |
| 49 | return 4; |
| 50 | case 3: |
| 51 | return 3; |
| 52 | case 4: |
| 53 | return 1; |
| 54 | case 5: |
| 55 | return 0; |
| 56 | default: |
| 57 | return -1; |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | static private int |
| 62 | paddingToBlockLen(int padlen) { |
| 63 | switch (padlen) { |
| 64 | case 6: |
| 65 | return 1; |
| 66 | case 4: |
| 67 | return 2; |
| 68 | case 3: |
| 69 | return 3; |
| 70 | case 1: |
| 71 | return 4; |
| 72 | case 0: |
| 73 | return 5; |
| 74 | default : |
| 75 | return -1; |
| 76 | } |
| 77 | } |
| 78 | |
| 79 | /** |
| 80 | * Convert binary data to a base32-encoded String |
| 81 | * |
| 82 | * @param b An array containing binary data |
| 83 | * @return A String containing the encoded data |
| 84 | */ |
| 85 | public String |
| 86 | toString(byte [] b) { |
| 87 | ByteArrayOutputStream os = new ByteArrayOutputStream(); |
| 88 | |
| 89 | for (int i = 0; i < (b.length + 4) / 5; i++) { |
| 90 | short s[] = new short[5]; |
| 91 | int t[] = new int[8]; |
| 92 | |
| 93 | int blocklen = 5; |
| 94 | for (int j = 0; j < 5; j++) { |
| 95 | if ((i * 5 + j) < b.length) |
| 96 | s[j] = (short) (b[i * 5 + j] & 0xFF); |
| 97 | else { |
| 98 | s[j] = 0; |
| 99 | blocklen--; |
| 100 | } |
| 101 | } |
| 102 | int padlen = blockLenToPadding(blocklen); |
| 103 | |
| 104 | // convert the 5 byte block into 8 characters (values 0-31). |
| 105 | |
| 106 | // upper 5 bits from first byte |
| 107 | t[0] = (byte) ((s[0] >> 3) & 0x1F); |
| 108 | // lower 3 bits from 1st byte, upper 2 bits from 2nd. |
| 109 | t[1] = (byte) (((s[0] & 0x07) << 2) | ((s[1] >> 6) & 0x03)); |
| 110 | // bits 5-1 from 2nd. |
| 111 | t[2] = (byte) ((s[1] >> 1) & 0x1F); |
| 112 | // lower 1 bit from 2nd, upper 4 from 3rd |
| 113 | t[3] = (byte) (((s[1] & 0x01) << 4) | ((s[2] >> 4) & 0x0F)); |
| 114 | // lower 4 from 3rd, upper 1 from 4th. |
| 115 | t[4] = (byte) (((s[2] & 0x0F) << 1) | ((s[3] >> 7) & 0x01)); |
| 116 | // bits 6-2 from 4th |
| 117 | t[5] = (byte) ((s[3] >> 2) & 0x1F); |
| 118 | // lower 2 from 4th, upper 3 from 5th; |
| 119 | t[6] = (byte) (((s[3] & 0x03) << 3) | ((s[4] >> 5) & 0x07)); |
| 120 | // lower 5 from 5th; |
| 121 | t[7] = (byte) (s[4] & 0x1F); |
| 122 | |
| 123 | // write out the actual characters. |
| 124 | for (int j = 0; j < t.length - padlen; j++) { |
| 125 | char c = alphabet.charAt(t[j]); |
| 126 | if (lowercase) |
| 127 | c = Character.toLowerCase(c); |
| 128 | os.write(c); |
| 129 | } |
| 130 | |
| 131 | // write out the padding (if any) |
| 132 | if (padding) { |
| 133 | for (int j = t.length - padlen; j < t.length; j++) |
| 134 | os.write('='); |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | return new String(os.toByteArray()); |
| 139 | } |
| 140 | |
| 141 | /** |
| 142 | * Convert a base32-encoded String to binary data |
| 143 | * |
| 144 | * @param str A String containing the encoded data |
| 145 | * @return An array containing the binary data, or null if the string is invalid |
| 146 | */ |
| 147 | public byte[] |
| 148 | fromString(String str) { |
| 149 | ByteArrayOutputStream bs = new ByteArrayOutputStream(); |
| 150 | byte [] raw = str.getBytes(); |
| 151 | for (int i = 0; i < raw.length; i++) |
| 152 | { |
| 153 | char c = (char) raw[i]; |
| 154 | if (!Character.isWhitespace(c)) { |
| 155 | c = Character.toUpperCase(c); |
| 156 | bs.write((byte) c); |
| 157 | } |
| 158 | } |
| 159 | |
| 160 | if (padding) { |
| 161 | if (bs.size() % 8 != 0) |
| 162 | return null; |
| 163 | } else { |
| 164 | while (bs.size() % 8 != 0) |
| 165 | bs.write('='); |
| 166 | } |
| 167 | |
| 168 | byte [] in = bs.toByteArray(); |
| 169 | |
| 170 | bs.reset(); |
| 171 | DataOutputStream ds = new DataOutputStream(bs); |
| 172 | |
| 173 | for (int i = 0; i < in.length / 8; i++) { |
| 174 | short[] s = new short[8]; |
| 175 | int[] t = new int[5]; |
| 176 | |
| 177 | int padlen = 8; |
| 178 | for (int j = 0; j < 8; j++) { |
| 179 | char c = (char) in[i * 8 + j]; |
| 180 | if (c == '=') |
| 181 | break; |
| 182 | s[j] = (short) alphabet.indexOf(in[i * 8 + j]); |
| 183 | if (s[j] < 0) |
| 184 | return null; |
| 185 | padlen--; |
| 186 | } |
| 187 | int blocklen = paddingToBlockLen(padlen); |
| 188 | if (blocklen < 0) |
| 189 | return null; |
| 190 | |
| 191 | // all 5 bits of 1st, high 3 (of 5) of 2nd |
| 192 | t[0] = (s[0] << 3) | s[1] >> 2; |
| 193 | // lower 2 of 2nd, all 5 of 3rd, high 1 of 4th |
| 194 | t[1] = ((s[1] & 0x03) << 6) | (s[2] << 1) | (s[3] >> 4); |
| 195 | // lower 4 of 4th, high 4 of 5th |
| 196 | t[2] = ((s[3] & 0x0F) << 4) | ((s[4] >> 1) & 0x0F); |
| 197 | // lower 1 of 5th, all 5 of 6th, high 2 of 7th |
| 198 | t[3] = (s[4] << 7) | (s[5] << 2) | (s[6] >> 3); |
| 199 | // lower 3 of 7th, all of 8th |
| 200 | t[4] = ((s[6] & 0x07) << 5) | s[7]; |
| 201 | |
| 202 | try { |
| 203 | for (int j = 0; j < blocklen; j++) |
| 204 | ds.writeByte((byte) (t[j] & 0xFF)); |
| 205 | } |
| 206 | catch (IOException e) { |
| 207 | } |
| 208 | } |
| 209 | |
| 210 | return bs.toByteArray(); |
| 211 | } |
| 212 | |
| 213 | } |