001/** 002 * Copyright 2011 Google Inc. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.syncany.util; 017 018import java.io.UnsupportedEncodingException; 019import java.security.MessageDigest; 020import java.security.NoSuchAlgorithmException; 021 022/** 023 * <p>Base58 is a way to encode Bitcoin addresses as numbers and letters. Note that this is not the same base58 as used by 024 * Flickr, which you may see reference to around the internet.</p> 025 * 026 * <p>Satoshi says: why base-58 instead of standard base-64 encoding?</p> 027 * 028 * <ul> 029 * <li>Don't want 0OIl characters that look the same in some fonts and 030 * could be used to create visually identical looking account numbers.</li> 031 * <li>A string with non-alphanumeric characters is not as easily accepted as an account number.</li> 032 * <li>E-mail usually won't line-break if there's no punctuation to break at.</li> 033 * <li>Doubleclicking selects the whole number as one word if it's all alphanumeric.</li> 034 * </ul> 035 * 036 * @see <a href="https://code.google.com/p/bitcoinj/source/browse/lib/src/com/google/bitcoin/core/Base58.java?r=216deb2d35d1a128a7f617b91f2ca35438aae546">Original source of this class</a> 037 * @author Mike Hearn 038 */ 039public class Base58 { 040 public static final char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray(); 041 private static final int[] INDEXES = new int[128]; 042 private static final MessageDigest digest; 043 044 static { 045 try { 046 digest = MessageDigest.getInstance("SHA-256"); 047 } catch (NoSuchAlgorithmException e) { 048 throw new RuntimeException(e); // Can't happen. 049 } 050 } 051 052 static { 053 for (int i = 0; i < INDEXES.length; i++) { 054 INDEXES[i] = -1; 055 } 056 for (int i = 0; i < ALPHABET.length; i++) { 057 INDEXES[ALPHABET[i]] = i; 058 } 059 } 060 061 /** 062 * Encodes the given bytes in base58. No checksum is appended. 063 */ 064 public static String encode(byte[] input) { 065 if (input.length == 0) { 066 return ""; 067 } 068 input = copyOfRange(input, 0, input.length); 069 // Count leading zeroes. 070 int zeroCount = 0; 071 while (zeroCount < input.length && input[zeroCount] == 0) { 072 ++zeroCount; 073 } 074 // The actual encoding. 075 byte[] temp = new byte[input.length * 2]; 076 int j = temp.length; 077 078 int startAt = zeroCount; 079 while (startAt < input.length) { 080 byte mod = divmod58(input, startAt); 081 if (input[startAt] == 0) { 082 ++startAt; 083 } 084 temp[--j] = (byte) ALPHABET[mod]; 085 } 086 087 // Strip extra '1' if there are some after decoding. 088 while (j < temp.length && temp[j] == ALPHABET[0]) { 089 ++j; 090 } 091 // Add as many leading '1' as there were leading zeros. 092 while (--zeroCount >= 0) { 093 temp[--j] = (byte) ALPHABET[0]; 094 } 095 096 byte[] output = copyOfRange(temp, j, temp.length); 097 try { 098 return new String(output, "US-ASCII"); 099 } catch (UnsupportedEncodingException e) { 100 throw new RuntimeException(e); // Cannot happen. 101 } 102 } 103 104 public static byte[] decode(String input) { 105 if (input.length() == 0) { 106 return new byte[0]; 107 } 108 byte[] input58 = new byte[input.length()]; 109 // Transform the String to a base58 byte sequence 110 for (int i = 0; i < input.length(); ++i) { 111 char c = input.charAt(i); 112 113 int digit58 = -1; 114 if (c >= 0 && c < 128) { 115 digit58 = INDEXES[c]; 116 } 117 if (digit58 < 0) { 118 throw new IllegalArgumentException("Illegal character " + c + " at " + i); 119 } 120 121 input58[i] = (byte) digit58; 122 } 123 // Count leading zeroes 124 int zeroCount = 0; 125 while (zeroCount < input58.length && input58[zeroCount] == 0) { 126 ++zeroCount; 127 } 128 // The encoding 129 byte[] temp = new byte[input.length()]; 130 int j = temp.length; 131 132 int startAt = zeroCount; 133 while (startAt < input58.length) { 134 byte mod = divmod256(input58, startAt); 135 if (input58[startAt] == 0) { 136 ++startAt; 137 } 138 139 temp[--j] = mod; 140 } 141 // Do no add extra leading zeroes, move j to first non null byte. 142 while (j < temp.length && temp[j] == 0) { 143 ++j; 144 } 145 146 return copyOfRange(temp, j - zeroCount, temp.length); 147 } 148 149 /** 150 * @see #doubleDigest(byte[], int, int) 151 */ 152 public static byte[] doubleDigest(byte[] input) { 153 return doubleDigest(input, 0, input.length); 154 } 155 156 /** 157 * Calculates the SHA-256 hash of the given byte range, and then hashes the resulting hash again. This is 158 * standard procedure in Bitcoin. The resulting hash is in big endian form. 159 */ 160 public static byte[] doubleDigest(byte[] input, int offset, int length) { 161 synchronized (digest) { 162 digest.reset(); 163 digest.update(input, offset, length); 164 byte[] first = digest.digest(); 165 return digest.digest(first); 166 } 167 } 168 169 // 170 // number -> number / 58, returns number % 58 171 // 172 private static byte divmod58(byte[] number, int startAt) { 173 int remainder = 0; 174 for (int i = startAt; i < number.length; i++) { 175 int digit256 = (int) number[i] & 0xFF; 176 int temp = remainder * 256 + digit256; 177 178 number[i] = (byte) (temp / 58); 179 180 remainder = temp % 58; 181 } 182 183 return (byte) remainder; 184 } 185 186 // 187 // number -> number / 256, returns number % 256 188 // 189 private static byte divmod256(byte[] number58, int startAt) { 190 int remainder = 0; 191 for (int i = startAt; i < number58.length; i++) { 192 int digit58 = (int) number58[i] & 0xFF; 193 int temp = remainder * 58 + digit58; 194 195 number58[i] = (byte) (temp / 256); 196 197 remainder = temp % 256; 198 } 199 200 return (byte) remainder; 201 } 202 203 private static byte[] copyOfRange(byte[] source, int from, int to) { 204 byte[] range = new byte[to - from]; 205 System.arraycopy(source, from, range, 0, range.length); 206 207 return range; 208 } 209}