From 7f997af8a685d300961de0e0e8d4b414243bb3ec Mon Sep 17 00:00:00 2001 From: jimbo Date: Fri, 30 Jul 2021 21:34:50 +0000 Subject: [PATCH] update --- README.md | 94 ++++++++++ backup_file/README.md | 94 ++++++++++ install.sh | 13 ++ restore.sh | 12 ++ upgrade_file/CryptEd25519.py | 340 +++++++++++++++++++++++++++++++++++ upgrade_file/CryptRsa.py | 65 +++++++ 6 files changed, 618 insertions(+) create mode 100644 README.md create mode 100644 backup_file/README.md create mode 100755 install.sh create mode 100755 restore.sh create mode 100644 upgrade_file/CryptEd25519.py create mode 100644 upgrade_file/CryptRsa.py diff --git a/README.md b/README.md new file mode 100644 index 0000000..827326d --- /dev/null +++ b/README.md @@ -0,0 +1,94 @@ +main reference: +- https://zeronet.io + - V3 mirror http://zeronet34m3r5ngdu54uj57dcafpgdjhxsgq5kla5con4qvcmfzpvhad.onion +- https://github.com/HelloZeroNet/ZeroNet + +# V3 Hidden Service support +sadly script with small patch to integrate support for hidden (onion) service V3 on ZeroNet darknet. + +this patch refers these issue: +- https://github.com/HelloZeroNet/ZeroNet/issues/2351 +- https://github.com/HelloZeroNet/ZeroNet/issues/1292 +- or from ZeroNet [GitCenter](http://127.0.0.1:43110/1GitLiXB6t5r8vuU2zC6a8GYj9ME6HMQ4t/repo/issues/view/?1P4w4Rvh4vS9r6G5xTiSh8qEW87EKZtnJB/9@1DfrA2M9Qra6stqT6tzwNMYNweswY54KAC) + +In particular it manages the tracker function ( bootstrap plugin enable ) verifying and distributing onion V3 peers to others ( to both patched and unpatched clients ). + +As you may know the hiddien service V2 are deprecated and in October will cease to exist permanently, is why in the transition phase (wait for V3 support to be released from the official repo) it's important to enable the boostrap plugin (and add v3 trackers in bootstrap list) to verify and share OnionV3 peers that cannot be verified by current trackers with the stable version. + + +For the installation guide i will use a fresh new client, following the installation guide on the official repo : https://github.com/HelloZeroNet/ZeroNet + +### Linux (x86-64bit) +- `wget https://github.com/HelloZeroNet/ZeroNet-linux/archive/dist-linux64/ZeroNet-py3-linux64.tar.gz` +- `tar xvpfz ZeroNet-py3-linux64.tar.gz` +- `cd ZeroNet-linux-dist-linux64/` + +now move inside core/ dir and download this repo +- `cd core` +- `git clone https://git.disroot.org/anonymoose/ZeroNet_OnionV3.git` + +move inside downloaded folder and launch `install.sh` script. +- `cd ZeroNet_OnionV3` +- `./install.sh` +back to main folder +- `cd ../..` + +now the client is enabled to support V3 hidden services +you can launch the client normally from ZeroNet-linux-dist-linux64 +- `./ZeroNet.sh` + +--- + +## Tor configuration and tracker settings +if you want to start immediately in tracker - tor always mode these are configurations tested on debian10, more information can be found in the official FAQ: +- https://zeronet.io/docs/faq/#how-to-use-zeronet-with-the-tor-browser +- https://zeronet.io/docs/faq/#how-to-use-zeronet-with-tor + +for the use described the commands to follow for the configuration of Tor are described here: +https://zeronet.io/docs/faq/#how-to-make-zeronet-work-with-tor-under-linuxmacos + +- `sudo nano /etc/tor/torrc` +- remove `#` character from lines `ControlPort 9051` and `CookieAuthentication 1` +- restart tor : `sudo service tor restart` +- add permission for yourself to read the auth cookie. with Debian Linux, the command is `sudo usermod -a -G debiantor [yourlinuxuser]` (if you are not on Debian check the file's user group by `ls -al /var/run/tor/control.authcookie`) +- logout/login with your user to apply group changes + +- add a `zeronet.conf` on ZeroNet-linux-dist-linux64 with +``` +[global] +tor = always +``` +- enable tracker plugin, always from ZeroNet-linux-dist-linux64 + `mv core/plugins/disabled-Bootstrapper core/plugins/Bootstrapper` +- launch `./ZeroNet.sh` + +--- + +## Tips +v3 tracker to add in bootstrap list: +- `zero://tnjppdoc4famqa62mldlzc3fqw2wlsfxov7iz3pz5ex6umnqpaupflqd.onion:16227` + +Syncronite trackers file list : +- http://127.0.0.1:43110/15CEFKBRHFfAP9rmL6hhLmHoXrrgmw4B5o +--- + +## Todo +- understand why onion addresses aren't shared as shared trackers +- permanent tracker onion address +- upgrade plugins/StemPort for V3 support +- default stem lib for manage hidden services + +## Patch explained +This patch doesn't definitively differentiate onion v2 from v3, via `CryptRsa.py` I've maintained the v2 functions unchanged and added a copy of those funcionts ( sign, verify key .. ) with library required by onion v3 (`Ed25519.py`), recognising the type of address version from the length of the hidden service keys used in the functions ( definitely could be improved! ). + + +## Script explained +- move `upgrade_file/CryptEd25519.py` and `upgrade_file/CryptRsa.py` to `ZeroPath/src/Crypt/` +- replace in `ZeroPath/src/Tor/TorManager.py` the 2 words `RSA1024` with `ED25519-V3` +- add the line to the top: + `from Crypt import CryptEd25519` to : + - `ZeroPath/src/Tor/TorManager.py` + - `ZeroPath/plugins/AnnounceZero/AnnounceZeroPlugin.py` + + +works diff --git a/backup_file/README.md b/backup_file/README.md new file mode 100644 index 0000000..ebb2edd --- /dev/null +++ b/backup_file/README.md @@ -0,0 +1,94 @@ +main reference: +- https://zeronet.io + - V3 mirror http://zeronet34m3r5ngdu54uj57dcafpgdjhxsgq5kla5con4qvcmfzpvhad.onion +- https://github.com/HelloZeroNet/ZeroNet + +# V3 Hidden Service support +sadly script with small patch to integrate support for hidden (onion) service V3 on ZeroNet darknet. + +this patch refers these issue: +- https://github.com/HelloZeroNet/ZeroNet/issues/2351 +- https://github.com/HelloZeroNet/ZeroNet/issues/1292 +- or from ZeroNet [GitCenter](http://127.0.0.1:43110/1GitLiXB6t5r8vuU2zC6a8GYj9ME6HMQ4t/repo/issues/view/?1P4w4Rvh4vS9r6G5xTiSh8qEW87EKZtnJB/9@1DfrA2M9Qra6stqT6tzwNMYNweswY54KAC) + +In particular it manages the tracker function ( bootstrap plugin enable ) verifying and distributing onion V3 peers to others ( to both patched and unpatched clients ). + +As you may know the hiddien service V2 are deprecated and in October will cease to exist permanently, is why in the transition phase (wait for V3 support to be released from the official repo) it's important to enable the boostrap plugin (and add v3 trackers in bootstrap list) to verify and share OnionV3 peers that cannot be verified by current trackers with the stable version. + + +For the installation guide i will use a fresh new client, following the installation guide on the official repo : https://github.com/HelloZeroNet/ZeroNet + +### Linux (x86-64bit) +- `wget https://github.com/HelloZeroNet/ZeroNet-linux/archive/dist-linux64/ZeroNet-py3-linux64.tar.gz` +- `tar xvpfz ZeroNet-py3-linux64.tar.gz` +- `cd ZeroNet-linux-dist-linux64/` + +now move inside core/ dir and download this repo +- `cd core` +- `git clone xxx` + +move inside downloaded folder and launch `install.sh` script. +- `cd xxx` +- `./install.sh` +back to main folder +- `cd ../..` + +now the client is enabled to support V3 hidden services +you can launch the client normally from ZeroNet-linux-dist-linux64 +- `./ZeroNet.sh` + +--- + +## Tor configuration and tracker settings +if you want to start immediately in tracker - tor always mode these are configurations tested on debian10, more information can be found in the official FAQ: +- https://zeronet.io/docs/faq/#how-to-use-zeronet-with-the-tor-browser +- https://zeronet.io/docs/faq/#how-to-use-zeronet-with-tor + +for the use described the commands to follow for the configuration of Tor are described here: +https://zeronet.io/docs/faq/#how-to-make-zeronet-work-with-tor-under-linuxmacos + +- `sudo nano /etc/tor/torrc` +- remove `#` character from lines `ControlPort 9051` and `CookieAuthentication 1` +- restart tor : `sudo service tor restart` +- add permission for yourself to read the auth cookie. with Debian Linux, the command is `sudo usermod -a -G debiantor [yourlinuxuser]` (if you are not on Debian check the file's user group by `ls -al /var/run/tor/control.authcookie`) +- logout/login with your user to apply group changes + +- add a `zeronet.conf` on ZeroNet-linux-dist-linux64 with +``` +[global] +tor = always +``` +- enable tracker plugin, always from ZeroNet-linux-dist-linux64 + `mv core/plugins/disabled-Bootstrapper core/plugins/Bootstrapper` +- launch `./ZeroNet.sh` + +--- + +## Tips +v3 tracker to add in bootstrap list: +- `zero://tnjppdoc4famqa62mldlzc3fqw2wlsfxov7iz3pz5ex6umnqpaupflqd.onion:16227i` + +Syncronite trackers file list : +- http://127.0.0.1:43110/15CEFKBRHFfAP9rmL6hhLmHoXrrgmw4B5o +--- + +## Todo +- understand why onion addresses aren't shared as shared trackers +- permanent tracker onion address +- upgrade plugins/StemPort for V3 support +- default stem lib for manage hidden services + +## Patch explained +This patch doesn't definitively differentiate onion v2 from v3, via `CryptRsa.py` I've maintained the v2 functions unchanged and added a copy of those funcionts ( sign, verify key .. ) with library required by onion v3 (`Ed25519.py`), recognising the type of address version from the length of the hidden service keys used in the functions ( definitely could be improved! ). + + +## Script explained +- move `upgrade_file/CryptEd25519.py` and `upgrade_file/CryptRsa.py` to `ZeroPath/src/Crypt/` +- replace in `ZeroPath/src/Tor/TorManager.py` the 2 words `RSA1024` with `ED25519-V3` +- add the line : + `from Crypt import CryptEd25519` to : + - `ZeroPath/src/Tor/TorManager.py` + - `ZeroPath/plugins/AnnounceZero/AnnounceZeroPlugin.py` + + +works diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..8944257 --- /dev/null +++ b/install.sh @@ -0,0 +1,13 @@ +#!/bin/bash +if [ -d $PWD/../src/ ] +then + sed -i '/from Crypt import CryptRsa/a from Crypt import CryptEd25519' $PWD/../src/Tor/TorManager.py + sed -i '/from Crypt import CryptRsa/a from Crypt import CryptEd25519' $PWD/../plugins/AnnounceZero/AnnounceZeroPlugin.py + sed -i 's/RSA1024/ED25519-V3/g' $PWD/../src/Tor/TorManager.py + cp $PWD/../src/Crypt/CryptRsa.py $PWD/backup_file/CryptRsa.py + cp $PWD/upgrade_file/CryptRsa.py $PWD/../src/Crypt/CryptRsa.py + cp $PWD/upgrade_file/CryptEd25519.py $PWD/../src/Crypt/CryptEd25519.py + echo "Good, all done!" +else + echo "! I was not executed in the right place" +fi diff --git a/restore.sh b/restore.sh new file mode 100755 index 0000000..55f5ae8 --- /dev/null +++ b/restore.sh @@ -0,0 +1,12 @@ +#!/bin/bash +if [ -d $PWD/../src/ ] +then + sed -i '/from Crypt import CryptEd25519/d' $PWD/../src/Tor/TorManager.py + sed -i '/from Crypt import CryptEd25519/d' $PWD/../plugins/AnnounceZero/AnnounceZeroPlugin.py + sed -i 's/ED25519-V3/RSA1024/g' $PWD/../src/Tor/TorManager.py + cp $PWD/backup_file/CryptRsa.py $PWD/../src/Crypt/CryptRsa.py + rm $PWD/../src/Crypt/CryptEd25519.py + echo "Good, all restored!" +else + echo "! I was not executed in the right place" +fi diff --git a/upgrade_file/CryptEd25519.py b/upgrade_file/CryptEd25519.py new file mode 100644 index 0000000..fc05a93 --- /dev/null +++ b/upgrade_file/CryptEd25519.py @@ -0,0 +1,340 @@ +## ZeroNet onion V3 support +## The following copied code is copied from stem.util.ed25519 official Tor Project python3 lib +## url : https://gitweb.torproject.org/stem.git/tree/stem/util/ed25519.py +## the ##modified tag means that the function has been modified respect to the one used by stem lib +## the ##custom tag means that the function has been added by me and it's not present on the stem ed25519.py file +## every comment i make begins with ## +## +# The following is copied from... +# +# https://github.com/pyca/ed25519 +# +# This is under the CC0 license. For more information please see... +# +# https://github.com/pyca/cryptography/issues/5068 + +# ed25519.py - Optimized version of the reference implementation of Ed25519 +# +# Written in 2011? by Daniel J. Bernstein +# 2013 by Donald Stufft +# 2013 by Alex Gaynor +# 2013 by Greg Price +# +# To the extent possible under law, the author(s) have dedicated all copyright +# and related and neighboring rights to this software to the public domain +# worldwide. This software is distributed without any warranty. +# +# You should have received a copy of the CC0 Public Domain Dedication along +# with this software. If not, see +# . + +""" +NB: This code is not safe for use with secret keys or secret data. +The only safe use of this code is for verifying signatures on public messages. + +Functions for computing the public key of a secret key and for signing +a message are included, namely publickey_unsafe and signature_unsafe, +for testing purposes only. + +The root of the problem is that Python's long-integer arithmetic is +not designed for use in cryptography. Specifically, it may take more +or less time to execute an operation depending on the values of the +inputs, and its memory access patterns may also depend on the inputs. +This opens it to timing and cache side-channel attacks which can +disclose data to an attacker. We rely on Python's long-integer +arithmetic, so we cannot handle secrets without risking their disclosure. +""" + +import hashlib +import operator +import sys +import base64 + + +__version__ = "1.0.dev0" + + +# Useful for very coarse version differentiation. +PY3 = sys.version_info[0] == 3 + +if PY3: + indexbytes = operator.getitem + intlist2bytes = bytes + int2byte = operator.methodcaller("to_bytes", 1, "big") +else: + int2byte = chr + range = xrange + + def indexbytes(buf, i): + return ord(buf[i]) + + def intlist2bytes(l): + return b"".join(chr(c) for c in l) + + +b = 256 +q = 2 ** 255 - 19 +l = 2 ** 252 + 27742317777372353535851937790883648493 + + +def H(m): + return hashlib.sha512(m).digest() + + +def pow2(x, p): + """== pow(x, 2**p, q)""" + while p > 0: + x = x * x % q + p -= 1 + return x + + +def inv(z): + """$= z^{-1} \mod q$, for z != 0""" + # Adapted from curve25519_athlon.c in djb's Curve25519. + z2 = z * z % q # 2 + z9 = pow2(z2, 2) * z % q # 9 + z11 = z9 * z2 % q # 11 + z2_5_0 = (z11 * z11) % q * z9 % q # 31 == 2^5 - 2^0 + z2_10_0 = pow2(z2_5_0, 5) * z2_5_0 % q # 2^10 - 2^0 + z2_20_0 = pow2(z2_10_0, 10) * z2_10_0 % q # ... + z2_40_0 = pow2(z2_20_0, 20) * z2_20_0 % q + z2_50_0 = pow2(z2_40_0, 10) * z2_10_0 % q + z2_100_0 = pow2(z2_50_0, 50) * z2_50_0 % q + z2_200_0 = pow2(z2_100_0, 100) * z2_100_0 % q + z2_250_0 = pow2(z2_200_0, 50) * z2_50_0 % q # 2^250 - 2^0 + return pow2(z2_250_0, 5) * z11 % q # 2^255 - 2^5 + 11 = q - 2 + + +d = -121665 * inv(121666) % q +I = pow(2, (q - 1) // 4, q) + + +def xrecover(y): + xx = (y * y - 1) * inv(d * y * y + 1) + x = pow(xx, (q + 3) // 8, q) + + if (x * x - xx) % q != 0: + x = (x * I) % q + + if x % 2 != 0: + x = q-x + + return x + + +By = 4 * inv(5) +Bx = xrecover(By) +B = (Bx % q, By % q, 1, (Bx * By) % q) +ident = (0, 1, 1, 0) + + +def edwards_add(P, Q): + # This is formula sequence 'addition-add-2008-hwcd-3' from + # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + (x1, y1, z1, t1) = P + (x2, y2, z2, t2) = Q + + a = (y1-x1)*(y2-x2) % q + b = (y1+x1)*(y2+x2) % q + c = t1*2*d*t2 % q + dd = z1*2*z2 % q + e = b - a + f = dd - c + g = dd + c + h = b + a + x3 = e*f + y3 = g*h + t3 = e*h + z3 = f*g + + return (x3 % q, y3 % q, z3 % q, t3 % q) + + +def edwards_double(P): + # This is formula sequence 'dbl-2008-hwcd' from + # http://www.hyperelliptic.org/EFD/g1p/auto-twisted-extended-1.html + (x1, y1, z1, t1) = P + + a = x1*x1 % q + b = y1*y1 % q + c = 2*z1*z1 % q + # dd = -a + e = ((x1+y1)*(x1+y1) - a - b) % q + g = -a + b # dd + b + f = g - c + h = -a - b # dd - b + x3 = e*f + y3 = g*h + t3 = e*h + z3 = f*g + + return (x3 % q, y3 % q, z3 % q, t3 % q) + + +def scalarmult(P, e): + if e == 0: + return ident + Q = scalarmult(P, e // 2) + Q = edwards_double(Q) + if e & 1: + Q = edwards_add(Q, P) + return Q + + +# Bpow[i] == scalarmult(B, 2**i) +Bpow = [] + + +def make_Bpow(): + P = B + for i in range(253): + Bpow.append(P) + P = edwards_double(P) +make_Bpow() + + +def scalarmult_B(e): + """ + Implements scalarmult(B, e) more efficiently. + """ + # scalarmult(B, l) is the identity + e = e % l + P = ident + for i in range(253): + if e & 1: + P = edwards_add(P, Bpow[i]) + e = e // 2 + assert e == 0, e + return P + + +def encodeint(y): + bits = [(y >> i) & 1 for i in range(b)] + return b''.join([ + int2byte(sum([bits[i * 8 + j] << j for j in range(8)])) + for i in range(b//8) + ]) + + +def encodepoint(P): + (x, y, z, t) = P + zi = inv(z) + x = (x * zi) % q + y = (y * zi) % q + bits = [(y >> i) & 1 for i in range(b - 1)] + [x & 1] + return b''.join([ + int2byte(sum([bits[i * 8 + j] << j for j in range(8)])) + for i in range(b // 8) + ]) + + +def bit(h, i): + return (indexbytes(h, i // 8) >> (i % 8)) & 1 + +##modified +def publickey_unsafe(sk): + """ + Not safe to use with secret keys or secret data. + + See module docstring. This function should be used for testing only. + """ + ##h = H(sk) + h = sk + a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) + A = scalarmult_B(a) + return encodepoint(A) + +##custom +## from stem.util.str_tools._to_unicode_impl +## from https://gitweb.torproject.org/stem.git/tree/stem/util/str_tools.py#n80 +def to_unicode_impl(msg): + if msg is not None and not isinstance(msg, str): + return msg.decode('utf-8', 'replace') + else: + return msg + +##custom +## rewritten stem.descriptor.hidden_service.address_from_identity_key +## from https://gitweb.torproject.org/stem.git/tree/stem/descriptor/hidden_service.py#n1088 +def publickey_to_onionaddress(key): + CHECKSUM_CONSTANT = b'.onion checksum' + ## version = stem.client.datatype.Size.CHAR.pack(3) + version = b'\x03' + checksum = hashlib.sha3_256(CHECKSUM_CONSTANT + key + version).digest()[:2] + onion_address = base64.b32encode(key + checksum + version) + return to_unicode_impl(onion_address + b'.onion').lower() + + +def Hint(m): + h = H(m) + return sum(2 ** i * bit(h, i) for i in range(2 * b)) + +##modified +def signature_unsafe(m, sk, pk): + """ + Not safe to use with secret keys or secret data. + + See module docstring. This function should be used for testing only. + """ + ##h = H(sk) + h = sk + a = 2 ** (b - 2) + sum(2 ** i * bit(h, i) for i in range(3, b - 2)) + r = Hint( + intlist2bytes([indexbytes(h, j) for j in range(b // 8, b // 4)]) + m + ) + R = scalarmult_B(r) + S = (r + Hint(encodepoint(R) + pk + m) * a) % l + return encodepoint(R) + encodeint(S) + + +def isoncurve(P): + (x, y, z, t) = P + return (z % q != 0 and + x*y % q == z*t % q and + (y*y - x*x - z*z - d*t*t) % q == 0) + + +def decodeint(s): + return sum(2 ** i * bit(s, i) for i in range(0, b)) + + +def decodepoint(s): + y = sum(2 ** i * bit(s, i) for i in range(0, b - 1)) + x = xrecover(y) + if x & 1 != bit(s, b-1): + x = q - x + P = (x, y, 1, (x*y) % q) + if not isoncurve(P): + raise ValueError("decoding point that is not on curve") + return P + + +class SignatureMismatch(Exception): + pass + + +def checkvalid(s, m, pk): + """ + Not safe to use when any argument is secret. + + See module docstring. This function should be used only for + verifying public signatures of public messages. + """ + if len(s) != b // 4: + raise ValueError("signature length is wrong") + + if len(pk) != b // 8: + raise ValueError("public-key length is wrong") + + R = decodepoint(s[:b // 8]) + A = decodepoint(pk) + S = decodeint(s[b // 8:b // 4]) + h = Hint(encodepoint(R) + pk + m) + + (x1, y1, z1, t1) = P = scalarmult_B(S) + (x2, y2, z2, t2) = Q = edwards_add(R, scalarmult(A, h)) + + if (not isoncurve(P) or not isoncurve(Q) or + (x1*z2 - x2*z1) % q != 0 or (y1*z2 - y2*z1) % q != 0): + raise SignatureMismatch("signature does not pass verification") diff --git a/upgrade_file/CryptRsa.py b/upgrade_file/CryptRsa.py new file mode 100644 index 0000000..02df8f4 --- /dev/null +++ b/upgrade_file/CryptRsa.py @@ -0,0 +1,65 @@ +import base64 +import hashlib + +def sign(data, privatekey): + import rsa + from rsa import pkcs1 + from Crypt import CryptEd25519 + ## v3 = 88 + if len(privatekey) == 88: + prv_key = base64.b64decode(privatekey) + pub_key = CryptEd25519.publickey_unsafe(prv_key) + sign = CryptEd25519.signature_unsafe(data, prv_key, pub_key) + return sign + + if "BEGIN RSA PRIVATE KEY" not in privatekey: + privatekey = "-----BEGIN RSA PRIVATE KEY-----\n%s\n-----END RSA PRIVATE KEY-----" % privatekey + + priv = rsa.PrivateKey.load_pkcs1(privatekey) + sign = rsa.pkcs1.sign(data, priv, 'SHA-256') + return sign + +def verify(data, publickey, sign): + import rsa + from rsa import pkcs1 + from Crypt import CryptEd25519 + + if len(publickey) == 32: + try: + valid = CryptEd25519.checkvalid(sign, data, publickey) + valid = 'SHA-256' + except Exception as err: + print(err) + valid = False + return valid + + pub = rsa.PublicKey.load_pkcs1(publickey, format="DER") + try: + valid = rsa.pkcs1.verify(data, sign, pub) + except pkcs1.VerificationError: + valid = False + return valid + +def privatekeyToPublickey(privatekey): + from Crypt import CryptEd25519 + import rsa + from rsa import pkcs1 + + if len(privatekey) == 88: + prv_key = base64.b64decode(privatekey) + pub_key = CryptEd25519.publickey_unsafe(prv_key) + return pub_key + + if "BEGIN RSA PRIVATE KEY" not in privatekey: + privatekey = "-----BEGIN RSA PRIVATE KEY-----\n%s\n-----END RSA PRIVATE KEY-----" % privatekey + + priv = rsa.PrivateKey.load_pkcs1(privatekey) + pub = rsa.PublicKey(priv.n, priv.e) + return pub.save_pkcs1("DER") + +def publickeyToOnion(publickey): + from Crypt import CryptEd25519 + if len(publickey) == 32: + addr = CryptEd25519.publickey_to_onionaddress(publickey)[:-6] + return addr + return base64.b32encode(hashlib.sha1(publickey).digest()[:10]).lower().decode("ascii")