forked from bitcoinj/bitcoinj
-
Notifications
You must be signed in to change notification settings - Fork 27
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changes to support connecting to .onion addresses
- PeerAddress detects .onion and serializes/deserializes them using the onioncat format, which is also used by bitcoin-core, btcd, and probably others. - Beginnings of a class for validating that peer addresses are routable. - PeerAddress hashCode() and equals(): add hostname and refactor. - PeerAddress: Use addr and hostname as alternatives. Cherry pick 9f09a89 Cherry pick 0988148 Cherry pick 32d9142 Cherry pick ebeecab
- Loading branch information
1 parent
d8c30c9
commit ad73b86
Showing
6 changed files
with
427 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
40 changes: 40 additions & 0 deletions
40
core/src/main/java/org/bitcoinj/net/OnionCatAddressChecker.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package org.bitcoinj.net; | ||
|
||
import org.bitcoinj.utils.CIDRUtils; | ||
|
||
import java.net.InetAddress; | ||
|
||
/** | ||
* Checks an IPv6 InetAddress represents a valid OnionCat address | ||
* @author danda | ||
* @author Oscar Guindzberg | ||
*/ | ||
public class OnionCatAddressChecker { | ||
|
||
// Note: this is borrowed/ported from btcd (written in go). | ||
|
||
// btcd has many more rules that are probably important and should be | ||
// implemented in this class, but for now we only care about onion | ||
// addresses for onioncat (ipv6) encoding/decoding. | ||
|
||
// onionCatNet defines the IPv6 address block used to support Tor. | ||
// bitcoind encodes a .onion address as a 16 byte number by decoding the | ||
// address prior to the .onion (i.e. the key hash) base32 into a ten | ||
// byte number. It then stores the first 6 bytes of the address as | ||
// 0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43. | ||
// | ||
// This is the same range used by OnionCat, which is part part of the | ||
// RFC4193 unique local IPv6 range. | ||
// | ||
// In summary the format is: | ||
// { magic 6 bytes, 10 bytes base32 decode of key hash } | ||
private static CIDRUtils onionCatNet = new CIDRUtils("fd87:d87e:eb43::", 48); | ||
|
||
// isOnionCatTor returns whether or not the passed address is in the IPv6 range | ||
// used by bitcoin to support Tor (fd87:d87e:eb43::/48). Note that this range | ||
// is the same range used by OnionCat, which is part of the RFC4193 unique local | ||
// IPv6 range. | ||
public static boolean isOnionCatTor(InetAddress addr) { | ||
return onionCatNet.isInRange(addr); | ||
} | ||
} |
61 changes: 61 additions & 0 deletions
61
core/src/main/java/org/bitcoinj/net/OnionCatConverter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package org.bitcoinj.net; | ||
|
||
import org.bitcoinj.core.Utils; | ||
import org.bitcoinj.utils.Base32; | ||
|
||
import java.net.InetAddress; | ||
import java.net.InetSocketAddress; | ||
import java.net.UnknownHostException; | ||
import java.util.Arrays; | ||
|
||
|
||
/** | ||
* Converts .onion addresses to IPv6 format and viceversa. | ||
* @author danda | ||
* @author Oscar Guindzberg | ||
*/ | ||
public class OnionCatConverter { | ||
|
||
/** Converts a .onion address to onioncat format | ||
* | ||
* @param hostname e.g. explorernuoc63nb.onion | ||
* @return e.g. fd87:d87e:eb43:25de:b744:916d:1c2f:6da1 | ||
*/ | ||
public static byte[] onionHostToIPV6Bytes(String hostname) { | ||
String needle = ".onion"; | ||
if(hostname.endsWith(needle)) { | ||
if (hostname.length() != 22) { | ||
throw new IllegalArgumentException("Invalid hostname: " + hostname); | ||
} | ||
hostname = hostname.substring(0, hostname.length() - needle.length()); | ||
} else { | ||
if (hostname.length() != 16) { | ||
throw new IllegalArgumentException("Invalid hostname: " + hostname); | ||
} | ||
} | ||
byte[] prefix = new byte[] {(byte)0xfd, (byte)0x87, (byte)0xd8, (byte)0x7e, (byte)0xeb, (byte)0x43}; | ||
byte[] onionaddr = Base32.base32Decode(hostname); | ||
byte[] ipBytes = new byte[prefix.length + onionaddr.length]; | ||
System.arraycopy(prefix, 0, ipBytes, 0, prefix.length); | ||
System.arraycopy(onionaddr, 0, ipBytes, prefix.length, onionaddr.length); | ||
|
||
return ipBytes; | ||
} | ||
|
||
public static InetAddress onionHostToInetAddress(String hostname) throws UnknownHostException { | ||
return InetAddress.getByAddress(onionHostToIPV6Bytes(hostname)); | ||
} | ||
|
||
|
||
/** Converts an IPV6 onioncat encoded address to a .onion address | ||
* @param bytes e.g. fd87:d87e:eb43:25de:b744:916d:1c2f:6da1 | ||
* @return e.g. explorernuoc63nb.onion | ||
*/ | ||
public static String IPV6BytesToOnionHost(byte[] bytes) { | ||
if (bytes.length != 16) { | ||
throw new IllegalArgumentException("Invalid IPv6 address: " + Utils.HEX.encode(bytes)); | ||
} | ||
String base32 = Base32.base32Encode( Arrays.copyOfRange(bytes, 6, 16) ); | ||
return base32.toLowerCase() + ".onion"; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
// Copied from orchid Tor lib. | ||
|
||
package org.bitcoinj.utils; | ||
|
||
public class Base32 { | ||
private final static String BASE32_CHARS = "abcdefghijklmnopqrstuvwxyz234567"; | ||
|
||
public static String base32Encode(byte[] source) { | ||
return base32Encode(source, 0, source.length); | ||
} | ||
|
||
public static String base32Encode(byte[] source, int offset, int length) { | ||
final int nbits = length * 8; | ||
if(nbits % 5 != 0) | ||
throw new IllegalArgumentException("Base32 input length must be a multiple of 5 bits"); | ||
|
||
final int outlen = nbits / 5; | ||
final StringBuffer outbuffer = new StringBuffer(); | ||
int bit = 0; | ||
for(int i = 0; i < outlen; i++) { | ||
int v = (source[bit / 8] & 0xFF) << 8; | ||
if(bit + 5 < nbits) v += (source[bit / 8 + 1] & 0xFF); | ||
int u = (v >> (11 - (bit % 8))) & 0x1F; | ||
outbuffer.append(BASE32_CHARS.charAt(u)); | ||
bit += 5; | ||
} | ||
return outbuffer.toString(); | ||
} | ||
|
||
public static byte[] base32Decode(String source) { | ||
int[] v = stringToIntVector(source); | ||
|
||
int nbits = source.length() * 5; | ||
if(nbits % 8 != 0) | ||
throw new IllegalArgumentException("Base32 decoded array must be a muliple of 8 bits"); | ||
|
||
int outlen = nbits / 8; | ||
byte[] outbytes = new byte[outlen]; | ||
|
||
int bit = 0; | ||
for(int i = 0; i < outlen; i++) { | ||
int bb = bit / 5; | ||
outbytes[i] = (byte) decodeByte(bit, v[bb], v[bb + 1], v[bb + 2]); | ||
bit += 8; | ||
} | ||
return outbytes; | ||
} | ||
|
||
private static int decodeByte(int bitOffset, int b0, int b1, int b2) { | ||
switch(bitOffset % 40) { | ||
case 0: | ||
return ls(b0, 3) + rs(b1, 2); | ||
case 8: | ||
return ls(b0, 6) + ls(b1, 1) + rs (b2, 4); | ||
case 16: | ||
return ls(b0, 4) + rs(b1, 1); | ||
case 24: | ||
return ls(b0, 7) + ls(b1, 2) + rs(b2, 3); | ||
case 32: | ||
return ls(b0, 5) + (b1 & 0xFF); | ||
} | ||
throw new IllegalArgumentException("Illegal bit offset"); | ||
} | ||
|
||
private static int ls(int n, int shift) { | ||
return ((n << shift) & 0xFF); | ||
} | ||
|
||
private static int rs(int n, int shift) { | ||
return ((n >> shift) & 0xFF); | ||
} | ||
|
||
private static int[] stringToIntVector(String s) { | ||
final int[] ints = new int[s.length() + 1]; | ||
for(int i = 0; i < s.length(); i++) { | ||
int b = s.charAt(i) & 0xFF; | ||
if(b > 0x60 && b < 0x7B) | ||
ints[i] = b - 0x61; | ||
else if(b > 0x31 && b < 0x38) | ||
ints[i] = b - 0x18; | ||
else if(b > 0x40 && b < 0x5B) | ||
ints[i] = b - 0x41; | ||
else | ||
throw new IllegalArgumentException("Illegal character in base32 encoded string: "+ s.charAt(i)); | ||
} | ||
return ints; | ||
} | ||
} |
Oops, something went wrong.