1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) 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 } 214