forked from sirMackk/ZeroNet
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Version 0.3.4, Rev656, CryptMessage plugin for AES and ECIES encrypti…
…on, Added pyelliptic lib for OpenSSSL based encryption methods, Test CryptMessage plugin, Force reload content.json before signing and after write, Escaped Sql IN queries support, Test Sql parameter escaping, ui_websocket Test fixture, Plugin testing support, Always return websocket errors as dict, Wait for file on weboscket fileGet command if its already in bad_files queue, PushState and ReplaceState url manipulation support in wrapper API, Per auth-address localstorage, Longer timeout for udp tracker query
- Loading branch information
1 parent
675bd46
commit ee70e2f
Showing
25 changed files
with
2,415 additions
and
43 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
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,53 @@ | ||
from lib.pybitcointools import bitcoin as btctools | ||
import hashlib | ||
|
||
ecc_cache = {} | ||
|
||
|
||
def encrypt(data, pubkey, ephemcurve=None, ciphername='aes-256-cbc'): | ||
from lib import pyelliptic | ||
curve, pubkey_x, pubkey_y, i = pyelliptic.ECC._decode_pubkey(pubkey) | ||
if ephemcurve is None: | ||
ephemcurve = curve | ||
ephem = pyelliptic.ECC(curve=ephemcurve) | ||
key = hashlib.sha512(ephem.raw_get_ecdh_key(pubkey_x, pubkey_y)).digest() | ||
key_e, key_m = key[:32], key[32:] | ||
pubkey = ephem.get_pubkey() | ||
iv = pyelliptic.OpenSSL.rand(pyelliptic.OpenSSL.get_cipher(ciphername).get_blocksize()) | ||
ctx = pyelliptic.Cipher(key_e, iv, 1, ciphername) | ||
ciphertext = iv + pubkey + ctx.ciphering(data) | ||
mac = pyelliptic.hmac_sha256(key_m, ciphertext) | ||
return key_e, ciphertext + mac | ||
|
||
|
||
def split(encrypted): | ||
iv = encrypted[0:16] | ||
ciphertext = encrypted[16+70:-32] | ||
|
||
return iv, ciphertext | ||
|
||
|
||
def getEcc(privatekey=None): | ||
from lib import pyelliptic | ||
global eccs | ||
if privatekey not in ecc_cache: | ||
if privatekey: | ||
publickey_bin = btctools.encode_pubkey(btctools.privtopub(privatekey), "bin") | ||
publickey_openssl = toOpensslPublickey(publickey_bin) | ||
privatekey_openssl = toOpensslPrivatekey(privatekey) | ||
ecc_cache[privatekey] = pyelliptic.ECC(curve='secp256k1', privkey=privatekey_openssl, pubkey=publickey_openssl) | ||
else: | ||
ecc_cache[None] = pyelliptic.ECC() | ||
return ecc_cache[privatekey] | ||
|
||
|
||
def toOpensslPrivatekey(privatekey): | ||
privatekey_bin = btctools.encode_privkey(privatekey, "bin") | ||
return '\x02\xca\x00\x20' + privatekey_bin | ||
|
||
|
||
def toOpensslPublickey(publickey): | ||
publickey_bin = btctools.encode_pubkey(publickey, "bin") | ||
publickey_bin = publickey_bin[1:] | ||
publickey_openssl = '\x02\xca\x00 ' + publickey_bin[:32] + '\x00 ' + publickey_bin[32:] | ||
return publickey_openssl |
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,149 @@ | ||
import base64 | ||
import os | ||
|
||
from Plugin import PluginManager | ||
from Crypt import CryptBitcoin | ||
from lib.pybitcointools import bitcoin as btctools | ||
|
||
import CryptMessage | ||
|
||
|
||
@PluginManager.registerTo("UiWebsocket") | ||
class UiWebsocketPlugin(object): | ||
def encrypt(self, text, publickey): | ||
encrypted = CryptMessage.encrypt(text, CryptMessage.toOpensslPublickey(publickey)) | ||
return encrypted | ||
|
||
def decrypt(self, encrypted, privatekey): | ||
back = CryptMessage.getEcc(privatekey).decrypt(encrypted) | ||
return back.decode("utf8") | ||
|
||
# - Actions - | ||
|
||
# Returns user's public key unique to site | ||
# Return: Public key | ||
def actionUserPublickey(self, to, index=0): | ||
publickey = self.user.getEncryptPublickey(self.site.address, index) | ||
self.response(to, publickey) | ||
|
||
# Encrypt a text using the publickey or user's sites unique publickey | ||
# Return: Encrypted text using base64 encoding | ||
def actionEciesEncrypt(self, to, text, publickey=0, return_aes_key=False): | ||
if type(publickey) is int: # Encrypt using user's publickey | ||
publickey = self.user.getEncryptPublickey(self.site.address, publickey) | ||
aes_key, encrypted = self.encrypt(text.encode("utf8"), publickey.decode("base64")) | ||
if return_aes_key: | ||
self.response(to, [base64.b64encode(encrypted), base64.b64encode(aes_key)]) | ||
else: | ||
self.response(to, base64.b64encode(encrypted)) | ||
|
||
# Decrypt a text using privatekey or the user's site unique private key | ||
# Return: Decrypted text or list of decrypted texts | ||
def actionEciesDecrypt(self, to, param, privatekey=0): | ||
if type(privatekey) is int: # Decrypt using user's privatekey | ||
privatekey = self.user.getEncryptPrivatekey(self.site.address, privatekey) | ||
|
||
if type(param) == list: | ||
encrypted_texts = param | ||
else: | ||
encrypted_texts = [param] | ||
|
||
texts = [] # Decoded texts | ||
for encrypted_text in encrypted_texts: | ||
try: | ||
text = self.decrypt(encrypted_text.decode("base64"), privatekey) | ||
texts.append(text) | ||
except Exception, err: | ||
texts.append(None) | ||
|
||
if type(param) == list: | ||
self.response(to, texts) | ||
else: | ||
self.response(to, texts[0]) | ||
|
||
# Encrypt a text using AES | ||
# Return: Iv, AES key, Encrypted text | ||
def actionAesEncrypt(self, to, text, key=None, iv=None): | ||
from lib import pyelliptic | ||
|
||
if key: | ||
key = key.decode("base64") | ||
else: | ||
key = os.urandom(32) | ||
|
||
if iv: # Generate new AES key if not definied | ||
iv = iv.decode("base64") | ||
else: | ||
iv = pyelliptic.Cipher.gen_IV('aes-256-cbc') | ||
|
||
if text: | ||
encrypted = pyelliptic.Cipher(key, iv, 1, ciphername='aes-256-cbc').ciphering(text.encode("utf8")) | ||
else: | ||
encrypted = "" | ||
|
||
self.response(to, [base64.b64encode(key), base64.b64encode(iv), base64.b64encode(encrypted)]) | ||
|
||
# Decrypt a text using AES | ||
# Return: Decrypted text | ||
def actionAesDecrypt(self, to, *args): | ||
from lib import pyelliptic | ||
|
||
if len(args) == 3: # Single decrypt | ||
encrypted_texts = [(args[0], args[1])] | ||
keys = [args[2]] | ||
else: # Batch decrypt | ||
encrypted_texts, keys = args | ||
|
||
texts = [] # Decoded texts | ||
for iv, encrypted_text in encrypted_texts: | ||
encrypted_text = encrypted_text.decode("base64") | ||
iv = iv.decode("base64") | ||
text = None | ||
for key in keys: | ||
ctx = pyelliptic.Cipher(key.decode("base64"), iv, 0, ciphername='aes-256-cbc') | ||
try: | ||
decrypted = ctx.ciphering(encrypted_text) | ||
if decrypted and decrypted.decode("utf8"): # Valid text decoded | ||
text = decrypted | ||
except Exception, err: | ||
pass | ||
texts.append(text) | ||
|
||
if len(args) == 3: | ||
self.response(to, texts[0]) | ||
else: | ||
self.response(to, texts) | ||
|
||
|
||
@PluginManager.registerTo("User") | ||
class UserPlugin(object): | ||
def getEncryptPrivatekey(self, address, param_index=0): | ||
assert param_index >= 0 and param_index <= 1000 | ||
site_data = self.getSiteData(address) | ||
|
||
if site_data.get("cert"): # Different privatekey for different cert provider | ||
index = param_index + self.getAddressAuthIndex(site_data["cert"]) | ||
else: | ||
index = param_index | ||
|
||
if "encrypt_privatekey_%s" % index not in site_data: | ||
address_index = self.getAddressAuthIndex(address) | ||
crypt_index = address_index + 1000 + index | ||
site_data["encrypt_privatekey_%s" % index] = CryptBitcoin.hdPrivatekey(self.master_seed, crypt_index) | ||
self.log.debug("New encrypt privatekey generated for %s:%s" % (address, index)) | ||
return site_data["encrypt_privatekey_%s" % index] | ||
|
||
def getEncryptPublickey(self, address, param_index=0): | ||
assert param_index >= 0 and param_index <= 1000 | ||
site_data = self.getSiteData(address) | ||
|
||
if site_data.get("cert"): # Different privatekey for different cert provider | ||
index = param_index + self.getAddressAuthIndex(site_data["cert"]) | ||
else: | ||
index = param_index | ||
|
||
if "encrypt_publickey_%s" % index not in site_data: | ||
privatekey = self.getEncryptPrivatekey(address, param_index) | ||
publickey = btctools.encode_pubkey(btctools.privtopub(privatekey), "bin_compressed") | ||
site_data["encrypt_publickey_%s" % index] = base64.b64encode(publickey) | ||
return site_data["encrypt_publickey_%s" % index] |
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,106 @@ | ||
import pytest | ||
from CryptMessage import CryptMessage | ||
|
||
@pytest.mark.usefixtures("resetSettings") | ||
class TestCrypt: | ||
def testPublickey(self, ui_websocket): | ||
pub = ui_websocket.testAction("UserPublickey", 0) | ||
assert len(pub) == 44 # Compressed, b64 encoded publickey | ||
|
||
# Different pubkey for specificed index | ||
assert ui_websocket.testAction("UserPublickey", 1) != ui_websocket.testAction("UserPublickey", 0) | ||
|
||
# Same publickey for same index | ||
assert ui_websocket.testAction("UserPublickey", 2) == ui_websocket.testAction("UserPublickey", 2) | ||
|
||
# Different publickey for different cert | ||
pub1 = ui_websocket.testAction("UserPublickey", 0) | ||
site_data = ui_websocket.user.getSiteData(ui_websocket.site.address) | ||
site_data["cert"] = "zeroid.bit" | ||
pub2 = ui_websocket.testAction("UserPublickey", 0) | ||
assert pub1 != pub2 | ||
|
||
|
||
|
||
def testEcies(self, ui_websocket): | ||
ui_websocket.actionUserPublickey(0, 0) | ||
pub = ui_websocket.ws.result | ||
|
||
ui_websocket.actionEciesEncrypt(0, "hello", pub) | ||
encrypted = ui_websocket.ws.result | ||
assert len(encrypted) == 180 | ||
|
||
# Don't allow decrypt using other privatekey index | ||
ui_websocket.actionEciesDecrypt(0, encrypted, 123) | ||
decrypted = ui_websocket.ws.result | ||
assert decrypted != "hello" | ||
|
||
# Decrypt using correct privatekey | ||
ui_websocket.actionEciesDecrypt(0, encrypted) | ||
decrypted = ui_websocket.ws.result | ||
assert decrypted == "hello" | ||
|
||
# Decrypt batch | ||
ui_websocket.actionEciesDecrypt(0, [encrypted, "baad", encrypted]) | ||
decrypted = ui_websocket.ws.result | ||
assert decrypted == ["hello", None, "hello"] | ||
|
||
|
||
def testEciesUtf8(self, ui_websocket): | ||
# Utf8 test | ||
utf8_text = u'\xc1rv\xedzt\xfbr\xf5t\xfck\xf6rf\xfar\xf3g\xe9p' | ||
ui_websocket.actionEciesEncrypt(0, utf8_text) | ||
encrypted = ui_websocket.ws.result | ||
|
||
ui_websocket.actionEciesDecrypt(0, encrypted) | ||
assert ui_websocket.ws.result == utf8_text | ||
|
||
|
||
def testEciesAes(self, ui_websocket): | ||
ui_websocket.actionEciesEncrypt(0, "hello", return_aes_key=True) | ||
ecies_encrypted, aes_key = ui_websocket.ws.result | ||
|
||
# Decrypt using Ecies | ||
ui_websocket.actionEciesDecrypt(0, ecies_encrypted) | ||
assert ui_websocket.ws.result == "hello" | ||
|
||
# Decrypt using AES | ||
aes_iv, aes_encrypted = CryptMessage.split(ecies_encrypted.decode("base64")) | ||
|
||
ui_websocket.actionAesDecrypt(0, aes_iv.encode("base64"), aes_encrypted.encode("base64"), aes_key) | ||
assert ui_websocket.ws.result == "hello" | ||
|
||
|
||
def testAes(self, ui_websocket): | ||
ui_websocket.actionAesEncrypt(0, "hello") | ||
key, iv, encrypted = ui_websocket.ws.result | ||
|
||
assert len(key) == 44 | ||
assert len(iv) == 24 | ||
assert len(encrypted) == 24 | ||
|
||
# Single decrypt | ||
ui_websocket.actionAesDecrypt(0, iv, encrypted, key) | ||
assert ui_websocket.ws.result == "hello" | ||
|
||
# Batch decrypt | ||
ui_websocket.actionAesEncrypt(0, "hello") | ||
key2, iv2, encrypted2 = ui_websocket.ws.result | ||
|
||
assert [key, iv, encrypted] != [key2, iv2, encrypted2] | ||
|
||
# 2 correct key | ||
ui_websocket.actionAesDecrypt(0, [[iv, encrypted], [iv, encrypted], [iv, "baad"], [iv2, encrypted2]], [key]) | ||
assert ui_websocket.ws.result == ["hello", "hello", None, None] | ||
|
||
# 3 key | ||
ui_websocket.actionAesDecrypt(0, [[iv, encrypted], [iv, encrypted], [iv, "baad"], [iv2, encrypted2]], [key, key2]) | ||
assert ui_websocket.ws.result == ["hello", "hello", None, "hello"] | ||
|
||
def testAesUtf8(self, ui_websocket): | ||
utf8_text = u'\xc1rv\xedzt\xfbr\xf5t\xfck\xf6rf\xfar\xf3g\xe9' | ||
ui_websocket.actionAesEncrypt(0, utf8_text) | ||
key, iv, encrypted = ui_websocket.ws.result | ||
|
||
ui_websocket.actionAesDecrypt(0, iv, encrypted, key) | ||
assert ui_websocket.ws.result == utf8_text |
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 @@ | ||
from src.Test.conftest import * |
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,5 @@ | ||
[pytest] | ||
python_files = Test*.py | ||
addopts = -rsxX -v --durations=6 | ||
markers = | ||
webtest: mark a test as a webtest. |
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 @@ | ||
import CryptMessagePlugin |
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
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
Oops, something went wrong.