mirror of
https://github.com/bitcoin-s/bitcoin-s.git
synced 2024-11-19 01:40:55 +01:00
Drop support for Tor v2 (#4864)
This commit is contained in:
parent
47d2d5a711
commit
4c0e6d5201
@ -63,60 +63,11 @@ class TorProtocolHandlerSpec
|
||||
println(address)
|
||||
}*/
|
||||
|
||||
test("happy path v2") {
|
||||
val promiseOnionAddress = Promise[InetSocketAddress]()
|
||||
|
||||
val protocolHandler = TestActorRef(
|
||||
props(version = OnionServiceVersion("v2"),
|
||||
authentication = Password(PASSWORD),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
onionAdded = Some(promiseOnionAddress)))
|
||||
|
||||
protocolHandler ! Connected(LocalHost, LocalHost)
|
||||
|
||||
expectMsg(ByteString("PROTOCOLINFO 1\r\n"))
|
||||
protocolHandler ! ByteString(
|
||||
"250-PROTOCOLINFO 1\r\n" +
|
||||
"250-AUTH METHODS=HASHEDPASSWORD\r\n" +
|
||||
"250-VERSION Tor=\"0.3.3.5\"\r\n" +
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
expectMsg(ByteString(s"""AUTHENTICATE "$PASSWORD"\r\n"""))
|
||||
protocolHandler ! ByteString(
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
expectMsg(ByteString("ADD_ONION NEW:RSA1024 Port=9999,9999\r\n"))
|
||||
protocolHandler ! ByteString(
|
||||
"250-ServiceID=z4zif3fy7fe7bpg3\r\n" +
|
||||
"250-PrivateKey=RSA1024:private-key\r\n" +
|
||||
"250 OK\r\n"
|
||||
)
|
||||
protocolHandler ! GetOnionAddress
|
||||
val addr = expectMsgType[Option[InetSocketAddress]]
|
||||
|
||||
assert(addr.nonEmpty)
|
||||
|
||||
assertAddressesEqual(
|
||||
addr.get,
|
||||
InetSocketAddress.createUnresolved("z4zif3fy7fe7bpg3.onion", 9999))
|
||||
|
||||
val address = Await.result(promiseOnionAddress.future, 3.seconds)
|
||||
assertAddressesEqual(
|
||||
address,
|
||||
InetSocketAddress.createUnresolved("z4zif3fy7fe7bpg3.onion", 9999))
|
||||
|
||||
assert(readString(PkFilePath) === "RSA1024:private-key")
|
||||
}
|
||||
|
||||
test("happy path v3") {
|
||||
val promiseOnionAddress = Promise[InetSocketAddress]()
|
||||
|
||||
val protocolHandler = TestActorRef(
|
||||
props(version = OnionServiceVersion("v3"),
|
||||
authentication = Password(PASSWORD),
|
||||
props(authentication = Password(PASSWORD),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
onionAdded = Some(promiseOnionAddress)))
|
||||
@ -164,22 +115,44 @@ class TorProtocolHandlerSpec
|
||||
assert(readString(PkFilePath) === "ED25519-V3:private-key")
|
||||
}
|
||||
|
||||
test("v2/v3 compatibility check against tor version") {
|
||||
assert(OnionServiceVersion.isCompatible(V3, "0.3.3.6"))
|
||||
assert(!OnionServiceVersion.isCompatible(V3, "0.3.3.5"))
|
||||
assert(OnionServiceVersion.isCompatible(V3, "0.3.3.6-devel"))
|
||||
assert(OnionServiceVersion.isCompatible(V3, "0.4"))
|
||||
assert(!OnionServiceVersion.isCompatible(V3, "0.2"))
|
||||
assert(OnionServiceVersion.isCompatible(V3, "0.5.1.2.3.4"))
|
||||
test("compatibility check against tor version") {
|
||||
assert(OnionServiceVersion.isCompatible("0.3.3.6"))
|
||||
assert(!OnionServiceVersion.isCompatible("0.3.3.5"))
|
||||
assert(OnionServiceVersion.isCompatible("0.3.3.6-devel"))
|
||||
assert(OnionServiceVersion.isCompatible("0.4"))
|
||||
assert(!OnionServiceVersion.isCompatible("0.2"))
|
||||
assert(OnionServiceVersion.isCompatible("0.5.1.2.3.4"))
|
||||
}
|
||||
|
||||
test("handle unsupported Tor version") {
|
||||
val promiseOnionAddress = Promise[InetSocketAddress]()
|
||||
|
||||
val protocolHandler = TestActorRef(
|
||||
props(authentication = Password(PASSWORD),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
onionAdded = Some(promiseOnionAddress)))
|
||||
|
||||
protocolHandler ! Connected(LocalHost, LocalHost)
|
||||
|
||||
expectMsg(ByteString("PROTOCOLINFO 1\r\n"))
|
||||
protocolHandler ! ByteString(
|
||||
"250-PROTOCOLINFO 1\r\n" +
|
||||
"250-AUTH METHODS=HASHEDPASSWORD\r\n" +
|
||||
"250-VERSION Tor=\"0.3.3.5\"\r\n" +
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
assert(intercept[TorException] {
|
||||
Await.result(promiseOnionAddress.future, 3.seconds)
|
||||
} === TorException("Tor version 0.3.3.5 is not supported"))
|
||||
}
|
||||
|
||||
test("authentication method errors") {
|
||||
val promiseOnionAddress = Promise[InetSocketAddress]()
|
||||
|
||||
val protocolHandler = TestActorRef(
|
||||
props(version = OnionServiceVersion("v2"),
|
||||
authentication = Password(PASSWORD),
|
||||
props(authentication = Password(PASSWORD),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
onionAdded = Some(promiseOnionAddress)))
|
||||
@ -190,7 +163,7 @@ class TorProtocolHandlerSpec
|
||||
protocolHandler ! ByteString(
|
||||
"250-PROTOCOLINFO 1\r\n" +
|
||||
"250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"" + CookieFilePath + "\"\r\n" +
|
||||
"250-VERSION Tor=\"0.3.3.5\"\r\n" +
|
||||
"250-VERSION Tor=\"0.4.7.10\"\r\n" +
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
@ -206,13 +179,10 @@ class TorProtocolHandlerSpec
|
||||
Files.write(CookieFilePath, CryptoUtil.randomBytes(32).toArray)
|
||||
|
||||
val protocolHandler = TestActorRef(
|
||||
props(
|
||||
version = OnionServiceVersion("v2"),
|
||||
authentication = SafeCookie(ClientNonce),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
onionAdded = Some(promiseOnionAddress)
|
||||
))
|
||||
props(authentication = SafeCookie(ClientNonce),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
onionAdded = Some(promiseOnionAddress)))
|
||||
|
||||
protocolHandler ! Connected(LocalHost, LocalHost)
|
||||
|
||||
@ -220,7 +190,7 @@ class TorProtocolHandlerSpec
|
||||
protocolHandler ! ByteString(
|
||||
"250-PROTOCOLINFO 1\r\n" +
|
||||
"250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"" + CookieFilePath + "\"\r\n" +
|
||||
"250-VERSION Tor=\"0.3.3.5\"\r\n" +
|
||||
"250-VERSION Tor=\"0.4.7.10\"\r\n" +
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
@ -242,7 +212,6 @@ class TorProtocolHandlerSpec
|
||||
|
||||
val protocolHandler = TestActorRef(
|
||||
props(
|
||||
version = OnionServiceVersion("v2"),
|
||||
authentication = SafeCookie(ClientNonce),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
@ -255,7 +224,7 @@ class TorProtocolHandlerSpec
|
||||
protocolHandler ! ByteString(
|
||||
"250-PROTOCOLINFO 1\r\n" +
|
||||
"250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"" + CookieFilePath + "\"\r\n" +
|
||||
"250-VERSION Tor=\"0.3.3.5\"\r\n" +
|
||||
"250-VERSION Tor=\"0.4.7.10\"\r\n" +
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
@ -284,7 +253,6 @@ class TorProtocolHandlerSpec
|
||||
|
||||
val protocolHandler = TestActorRef(
|
||||
props(
|
||||
version = OnionServiceVersion("v2"),
|
||||
authentication = SafeCookie(ClientNonce),
|
||||
privateKeyPath = PkFilePath,
|
||||
virtualPort = 9999,
|
||||
@ -297,7 +265,7 @@ class TorProtocolHandlerSpec
|
||||
protocolHandler ! ByteString(
|
||||
"250-PROTOCOLINFO 1\r\n" +
|
||||
"250-AUTH METHODS=COOKIE,SAFECOOKIE COOKIEFILE=\"" + CookieFilePath + "\"\r\n" +
|
||||
"250-VERSION Tor=\"0.3.3.5\"\r\n" +
|
||||
"250-VERSION Tor=\"0.4.7.10\"\r\n" +
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
@ -313,7 +281,7 @@ class TorProtocolHandlerSpec
|
||||
"250 OK\r\n"
|
||||
)
|
||||
|
||||
expectMsg(ByteString("ADD_ONION NEW:RSA1024 Port=9999,9999\r\n"))
|
||||
expectMsg(ByteString("ADD_ONION NEW:ED25519-V3 Port=9999,9999\r\n"))
|
||||
protocolHandler ! ByteString(
|
||||
"513 Invalid argument\r\n"
|
||||
)
|
||||
|
@ -118,7 +118,6 @@ object TorController extends Logging {
|
||||
val promiseConnected = Promise[Done]()
|
||||
|
||||
val protocolHandlerProps = TorProtocolHandler.props(
|
||||
version = TorProtocolHandler.V3,
|
||||
authentication = authentication,
|
||||
privateKeyPath = privateKeyPath,
|
||||
virtualPort = virtualPort,
|
||||
|
@ -4,7 +4,7 @@ import akka.actor.{Actor, ActorLogging, ActorRef, Props, Stash}
|
||||
import akka.io.Tcp.Connected
|
||||
import akka.util.ByteString
|
||||
import org.bitcoins.crypto.CryptoUtil
|
||||
import org.bitcoins.tor.TorProtocolHandler.{Authentication, OnionServiceVersion}
|
||||
import org.bitcoins.tor.TorProtocolHandler.Authentication
|
||||
import scodec.bits.ByteVector
|
||||
|
||||
import java.net.InetSocketAddress
|
||||
@ -23,7 +23,6 @@ case class TorException(private val msg: String)
|
||||
*
|
||||
* Specification: https://gitweb.torproject.org/torspec.git/tree/control-spec.txt
|
||||
*
|
||||
* @param onionServiceVersion v2 or v3
|
||||
* @param authentication Tor controller auth mechanism (password or safecookie)
|
||||
* @param privateKeyPath path to a file that contains a Tor private key
|
||||
* @param virtualPort port for the public hidden service (typically 9735)
|
||||
@ -31,7 +30,6 @@ case class TorException(private val msg: String)
|
||||
* @param onionAdded a Promise to track creation of the endpoint
|
||||
*/
|
||||
class TorProtocolHandler(
|
||||
onionServiceVersion: OnionServiceVersion,
|
||||
authentication: Authentication,
|
||||
privateKeyPath: Path,
|
||||
virtualPort: Int,
|
||||
@ -60,9 +58,8 @@ class TorProtocolHandler(
|
||||
val torVersion = unquote(
|
||||
res.getOrElse("Tor", throw TorException("version not found")))
|
||||
log.info(s"Tor version $torVersion")
|
||||
if (!OnionServiceVersion.isCompatible(onionServiceVersion, torVersion)) {
|
||||
throw TorException(
|
||||
s"version $torVersion does not support onion service $onionServiceVersion")
|
||||
if (!OnionServiceVersion.isCompatible(torVersion)) {
|
||||
throw TorException(s"Tor version $torVersion is not supported")
|
||||
}
|
||||
if (!Authentication.isCompatible(authentication, methods)) {
|
||||
throw TorException(
|
||||
@ -147,10 +144,7 @@ class TorProtocolHandler(
|
||||
if (privateKeyPath.toFile.exists()) {
|
||||
readString(privateKeyPath)
|
||||
} else {
|
||||
onionServiceVersion match {
|
||||
case V2 => "NEW:RSA1024"
|
||||
case V3 => "NEW:ED25519-V3"
|
||||
}
|
||||
"NEW:ED25519-V3"
|
||||
}
|
||||
}
|
||||
|
||||
@ -192,15 +186,13 @@ class TorProtocolHandler(
|
||||
object TorProtocolHandler {
|
||||
|
||||
def props(
|
||||
version: OnionServiceVersion,
|
||||
authentication: Authentication,
|
||||
privateKeyPath: Path,
|
||||
virtualPort: Int,
|
||||
targets: Seq[String] = Seq(),
|
||||
onionAdded: Option[Promise[InetSocketAddress]] = None): Props =
|
||||
Props(
|
||||
new TorProtocolHandler(version,
|
||||
authentication,
|
||||
new TorProtocolHandler(authentication,
|
||||
privateKeyPath,
|
||||
virtualPort,
|
||||
targets,
|
||||
@ -213,46 +205,28 @@ object TorProtocolHandler {
|
||||
private val ClientKey = ByteVector.view(
|
||||
"Tor safe cookie authentication controller-to-server hash".getBytes())
|
||||
|
||||
// @formatter:off
|
||||
sealed trait OnionServiceVersion
|
||||
case object V2 extends OnionServiceVersion
|
||||
case object V3 extends OnionServiceVersion
|
||||
// @formatter:on
|
||||
|
||||
object OnionServiceVersion {
|
||||
|
||||
def apply(s: String): OnionServiceVersion = s match {
|
||||
case "v2" | "V2" => V2
|
||||
case "v3" | "V3" => V3
|
||||
case _ => throw TorException(s"unknown protocol version `$s`")
|
||||
def isCompatible(torVersion: String): Boolean = {
|
||||
torVersion
|
||||
.split("\\.")
|
||||
.map(
|
||||
_.split('-').head
|
||||
) // remove non-numeric symbols at the end of the last number (rc, beta, alpha, etc.)
|
||||
.map(d => Try(d.toInt).getOrElse(0))
|
||||
.zipAll(List(0, 3, 3, 6), 0, 0) // min version for v3 is 0.3.3.6
|
||||
.foldLeft(Option.empty[Boolean]) { // compare subversion by subversion starting from the left
|
||||
case (Some(res), _) =>
|
||||
Some(
|
||||
res
|
||||
) // we stop the comparison as soon as there is a difference
|
||||
case (None, (v, vref)) =>
|
||||
if (v > vref) Some(true)
|
||||
else if (v < vref) Some(false)
|
||||
else None
|
||||
}
|
||||
.getOrElse(true) // if version == 0.3.3.6 then result will be None
|
||||
}
|
||||
|
||||
def isCompatible(
|
||||
onionServiceVersion: OnionServiceVersion,
|
||||
torVersion: String): Boolean =
|
||||
onionServiceVersion match {
|
||||
case V2 => true
|
||||
case V3 =>
|
||||
torVersion
|
||||
.split("\\.")
|
||||
.map(
|
||||
_.split('-').head
|
||||
) // remove non-numeric symbols at the end of the last number (rc, beta, alpha, etc.)
|
||||
.map(d => Try(d.toInt).getOrElse(0))
|
||||
.zipAll(List(0, 3, 3, 6), 0, 0) // min version for v3 is 0.3.3.6
|
||||
.foldLeft(Option.empty[Boolean]) { // compare subversion by subversion starting from the left
|
||||
case (Some(res), _) =>
|
||||
Some(
|
||||
res
|
||||
) // we stop the comparison as soon as there is a difference
|
||||
case (None, (v, vref)) =>
|
||||
if (v > vref) Some(true)
|
||||
else if (v < vref) Some(false)
|
||||
else None
|
||||
}
|
||||
.getOrElse(true) // if version == 0.3.3.6 then result will be None
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
sealed trait Authentication
|
||||
|
Loading…
Reference in New Issue
Block a user