Skip to content

Commit

Permalink
Release 0.23.13
Browse files Browse the repository at this point in the history
* receiver parameter removed from beempy decrypt
* beempy encrypt / decrypt is able to encryp/derypt a binary file
* encrypt_binary, decrypt_binary and extract_decrypt_memo_data added to beem.memo
* extract_memo_data added to beembase.memo
  • Loading branch information
holgern committed Jun 9, 2020
1 parent 87f462a commit 76d7f1c
Show file tree
Hide file tree
Showing 9 changed files with 284 additions and 46 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
Changelog
=========
0.23.13
-------
* receiver parameter removed from beempy decrypt
* beempy encrypt / decrypt is able to encryp/derypt a binary file
* encrypt_binary, decrypt_binary and extract_decrypt_memo_data added to beem.memo
* extract_memo_data added to beembase.memo

0.23.12
-------
* add participation_rate to Blockchain
Expand Down
93 changes: 78 additions & 15 deletions beem/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -2088,46 +2088,95 @@ def message(message_file, account, verify):


@cli.command()
@click.argument('sender', nargs=1)
@click.argument('memo', nargs=-1)
@click.option('--account', '-a', help='Account which decrypts the memo with its memo key')
@click.option('--output', '-o', help='Output file name. Result is stored, when set instead of printed.')
@click.option('--info', '-i', help='Shows information about public keys and used nonce', is_flag=True, default=False)
@click.option('--text', '-t', help='Reads the text file content', is_flag=True, default=False)
def decrypt(sender, memo, account, text):
@click.option('--binary', '-b', help='Reads the binary file content', is_flag=True, default=False)
def decrypt(memo, account, output, info, text, binary):
"""decrypt a (or more than one) decrypted memo/file with your memo key
"""
if text and binary:
print("You cannot set text and binary!")
return
stm = shared_blockchain_instance()
if stm.rpc is not None:
stm.rpc.rpcconnect()
if not account:
account = stm.config["default_account"]
m = Memo(from_account=sender, to_account=account, blockchain_instance=stm)
m = Memo(from_account=None, to_account=account, blockchain_instance=stm)

if not unlock_wallet(stm):
return
return
for entry in memo:
print("\n")
if not binary and info:
from_key, to_key, nonce = m.extract_decrypt_memo_data(entry)
try:
from_account = stm.wallet.getAccountFromPublicKey(str(from_key))
to_account = stm.wallet.getAccountFromPublicKey(str(to_key))
if from_account is not None:
print("from: %s" % str(from_account))
else:
print("from: %s" % str(from_key))
if to_account is not None:
print("to: %s" % str(to_account))
else:
print("to: %s" % str(to_key))
print("nonce: %s" % nonce)
except:
print("from: %s" % str(from_key))
print("to: %s" % str(to_key))
print("nonce: %s" % nonce)
if text:
with open(entry) as f:
message = f.read()
elif binary:
if output is None:
output = entry + ".dec"
ret = m.decrypt_binary(entry, output, buffer_size=2048)
if info:
t = PrettyTable(["Key", "Value"])
t.align = "l"
t.add_row(["file", entry])
for key in ret:
t.add_row([key, ret[key]])
print(t)
else:
message = entry
out = m.decrypt(message)
if text:
with open(entry, "w", encoding="utf-8") as f:
out = m.decrypt(message)
if output is None:
output = entry
with open(output, "w", encoding="utf-8") as f:
f.write(out)
else:
print(out)
elif not binary:
out = m.decrypt(message)
if info:
print("message: %s" % out)
if output:
with open(output, "w", encoding="utf-8") as f:
f.write(out)
elif not info:
print(out)


@cli.command()
@click.argument('receiver', nargs=1)
@click.argument('memo', nargs=-1)
@click.option('--account', '-a', help='Account which encrypts the memo with its memo key')
@click.option('--output', '-o', help='Output file name. Result is stored, when set instead of printed.')
@click.option('--text', '-t', help='Reads the text file content', is_flag=True, default=False)
def encrypt(receiver, memo, account, text):
@click.option('--binary', '-b', help='Reads the binary file content', is_flag=True, default=False)
def encrypt(receiver, memo, account, output, text, binary):
"""encrypt a (or more than one) memo text/file with the your memo key
"""
if text and binary:
print("You cannot set text and binary!")
return
stm = shared_blockchain_instance()
if stm.rpc is not None:
stm.rpc.rpcconnect()
Expand All @@ -2141,16 +2190,30 @@ def encrypt(receiver, memo, account, text):
if text:
with open(entry) as f:
message = f.read()
if message[0] == "#":
message = message[1:]
elif binary:
if output is None:
output = entry + ".enc"
m.encrypt_binary(entry, output, buffer_size=2048)
else:
message = entry
if message[0] == "#":
message = message[1:]
out = m.encrypt(message)["message"]
if message[0] == "#":
message = message[1:]

if text:
with open(entry, "w", encoding="utf-8") as f:
out = m.encrypt(message)["message"]
if output is None:
output = entry
with open(output, "w", encoding="utf-8") as f:
f.write(out)
else:
print(out)
elif not binary:
out = m.encrypt(message)["message"]
if output is None:
print(out)
else:
with open(output, "w", encoding="utf-8") as f:
f.write(out)


@cli.command()
Expand Down
194 changes: 178 additions & 16 deletions beem/memo.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@
from builtins import object
from beem.instance import shared_blockchain_instance
import random
import os
import struct
from binascii import hexlify, unhexlify
from beemgraphenebase.base58 import base58encode, base58decode
from beem.version import version as __version__
from beembase import memo as BtsMemo
from beemgraphenebase.account import PrivateKey, PublicKey
from .account import Account
Expand Down Expand Up @@ -149,8 +154,12 @@ def __init__(

if to_account:
self.to_account = Account(to_account, blockchain_instance=self.blockchain)
else:
self.to_account = None
if from_account:
self.from_account = Account(from_account, blockchain_instance=self.blockchain)
else:
self.from_account = None

def unlock_wallet(self, *args, **kwargs):
""" Unlock the library internal wallet
Expand Down Expand Up @@ -213,6 +222,63 @@ def encrypt(self, memo, bts_encrypt=False):
"to": self.to_account["memo_key"]
}

def encrypt_binary(self, infile, outfile, buffer_size=2048):
""" Encrypt a binary file
:param str infile: input file name
:param str outfile: output file name
:param int buffer_size: write buffer size
"""
if not os.path.exists(infile):
raise ValueError("%s does not exists!" % infile)

nonce = str(random.getrandbits(64))
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
self.from_account["memo_key"]
)
if not memo_wif:
raise MissingKeyError("Memo key for %s missing!" % self.from_account["name"])

if not hasattr(self, 'chain_prefix'):
self.chain_prefix = self.blockchain.prefix

file_size = os.path.getsize(infile)
priv = PrivateKey(memo_wif)
pub = PublicKey(
self.to_account["memo_key"],
prefix=self.chain_prefix
)
enc = BtsMemo.encode_memo(
priv,
pub,
nonce,
"beem/%s" % __version__,
prefix=self.chain_prefix
)
enc = unhexlify(base58decode(enc[1:]))
shared_secret = BtsMemo.get_shared_secret(priv, pub)
aes, check = BtsMemo.init_aes(shared_secret, nonce)
with open(outfile, 'wb') as fout:
fout.write(struct.pack('<Q', len(enc)))
fout.write(enc)
fout.write(struct.pack('<Q', file_size))
with open(infile, 'rb') as fin:
while True:
data = fin.read(buffer_size)
n = len(data)
if n == 0:
break
elif n % 16 != 0:
data += b' ' * (16 - n % 16) # <- padded with spaces
encd = aes.encrypt(data)
fout.write(encd)

def extract_decrypt_memo_data(self, memo):
""" Returns information about an encrypted memo
"""
from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(memo)
return from_key, to_key, nonce

def decrypt(self, memo):
""" Decrypt a memo
Expand All @@ -236,30 +302,51 @@ def decrypt(self, memo):
nonce = memo.get("nonce")
else:
nonce = ""

try:
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
memo_to["memo_key"]
)
pubkey = memo_from["memo_key"]
except MissingKeyError:

if memo_to is None or memo_from is None:
from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(message)
try:
# if that failed, we assume that we have sent the memo
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
memo_from["memo_key"]
str(to_key)
)
pubkey = memo_to["memo_key"]
pubkey = from_key
except MissingKeyError:
# if all fails, raise exception
raise MissingKeyError(
"Non of the required memo keys are installed!"
"Need any of {}".format(
[memo_to["name"], memo_from["name"]]))
try:
# if that failed, we assume that we have sent the memo
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
str(from_key)
)
pubkey = to_key
except MissingKeyError:
# if all fails, raise exception
raise MissingKeyError(
"Non of the required memo keys are installed!"
"Need any of {}".format(
[str(to_key), str(from_key)]))
else:
try:
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
memo_to["memo_key"]
)
pubkey = memo_from["memo_key"]
except MissingKeyError:
try:
# if that failed, we assume that we have sent the memo
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
memo_from["memo_key"]
)
pubkey = memo_to["memo_key"]
except MissingKeyError:
# if all fails, raise exception
raise MissingKeyError(
"Non of the required memo keys are installed!"
"Need any of {}".format(
[memo_to["name"], memo_from["name"]]))

if not hasattr(self, 'chain_prefix'):
self.chain_prefix = self.blockchain.prefix

if message[0] == '#':
if message[0] == '#' or memo_to is None or memo_from is None:
return BtsMemo.decode_memo(
PrivateKey(memo_wif),
message
Expand All @@ -271,3 +358,78 @@ def decrypt(self, memo):
nonce,
message
)

def decrypt_binary(self, infile, outfile, buffer_size=2048):
""" Decrypt a binary file
:param str infile: encrypted binary file
:param str outfile: output file name
:param int buffer_size: read buffer size
:returns: encrypted memo information
:rtype: dict
"""
if not os.path.exists(infile):
raise ValueError("%s does not exists!" % infile)
if buffer_size % 16 != 0:
raise ValueError("buffer_size must be dividable by 16")
with open(infile, 'rb') as fin:
memo_size = struct.unpack('<Q', fin.read(struct.calcsize('<Q')))[0]
memo = fin.read(memo_size)
orig_file_size = struct.unpack('<Q', fin.read(struct.calcsize('<Q')))[0]
memo = '#' + base58encode(hexlify(memo).decode("ascii"))
from_key, to_key, nonce, check, cipher = BtsMemo.extract_memo_data(memo)

try:
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
str(to_key)
)
pubkey = from_key
except MissingKeyError:
try:
# if that failed, we assume that we have sent the memo
memo_wif = self.blockchain.wallet.getPrivateKeyForPublicKey(
str(from_key)
)
pubkey = to_key
except MissingKeyError:
# if all fails, raise exception
raise MissingKeyError(
"Non of the required memo keys are installed!"
"Need any of {}".format(
[str(to_key), str(from_key)]))

if not hasattr(self, 'chain_prefix'):
self.chain_prefix = self.blockchain.prefix
priv = PrivateKey(memo_wif)
pubkey = PublicKey(pubkey, prefix=self.chain_prefix)
beem_version = BtsMemo.decode_memo(
priv,
memo
)
shared_secret = BtsMemo.get_shared_secret(priv, pubkey)
# Init encryption
aes, checksum = BtsMemo.init_aes(shared_secret, nonce)
with open(infile, 'rb') as fin:
memo_size = struct.unpack('<Q', fin.read(struct.calcsize('<Q')))[0]
memo = fin.read(memo_size)
file_size = struct.unpack('<Q', fin.read(struct.calcsize('<Q')))[0]
with open(outfile, 'wb') as fout:
while True:
data = fin.read(buffer_size)
n = len(data)
if n == 0:
break
decd = aes.decrypt(data)
n = len(decd)
if file_size > n:
fout.write(decd)
else:
fout.write(decd[:file_size]) # <- remove padding on last block
file_size -= n
return {
"file_size": orig_file_size,
"from_key": str(from_key),
"to_key": str(to_key),
"nonce": nonce,
"beem_version": beem_version
}
2 changes: 1 addition & 1 deletion beem/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""THIS FILE IS GENERATED FROM beem SETUP.PY."""
version = '0.23.12'
version = '0.23.13'
2 changes: 1 addition & 1 deletion beemapi/version.py
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
"""THIS FILE IS GENERATED FROM beem SETUP.PY."""
version = '0.23.12'
version = '0.23.13'
Loading

0 comments on commit 76d7f1c

Please sign in to comment.