Add an example script on assembling a message
All checks were successful
buildbot/multibuild_parent Build done.
buildbot/travis_bionic Build done.

This commit is contained in:
Lee Miller 2022-12-28 23:02:02 +02:00
parent 1db96e2e27
commit 43ba487d67
Signed by: lee.miller
GPG Key ID: 4F97A5EA88F4AB63

221
assemble_msg.py Normal file
View File

@ -0,0 +1,221 @@
import hashlib
import logging
import random
import time
from binascii import hexlify, unhexlify
from struct import pack
from pybitmessage import (
defaults, helper_msgcoding, highlevelcrypto, protocol, shared)
from pybitmessage.addresses import (
decodeAddress, decodeVarint, encodeAddress, encodeVarint)
from pybitmessage.fallback import RIPEMD160Hash
from pybitmessage.helper_ackPayload import genAckPayload
logger = logging.getLogger('default')
class Address(object):
version = 4
stream = 1
sigkey = None
enkey = None
ripe = None
bitfield = pack('>I', 0)
avg_pow = defaults.networkDefaultProofOfWorkNonceTrialsPerByte
avg_extra_bytes = defaults.networkDefaultPayloadLengthExtraBytes
@classmethod
def fromData(cls, data, addr=None):
# parse addr_data
# SELECT transmitdata FROM pubkeys WHERE address = ?
version, _ = decodeVarint(data[0])
pos = 1
stream, length = decodeVarint(data[pos:pos + 10])
pos += length
bitfield = data[pos:pos + 4]
# TODO: mobile
pos += 4
sigkey = data[pos:pos + 64]
pos += 64
enkey = data[pos:pos + 64]
pos += 64
if version >= 3:
avg_pow = decodeVarint(data[pos:pos + 10])
pos += length
extra_bytes, length = decodeVarint(data[pos:pos + 10])
avg_pow = max(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte, avg_pow)
extra_bytes = max(
defaults.networkDefaultPayloadLengthExtraBytes, extra_bytes)
else:
avg_pow = None
extra_bytes = None
return cls(
version, stream, sigkey, enkey,
addr=addr, avg_pow=avg_pow, extra_bytes=extra_bytes,
bitfield=bitfield)
def __init__(
self, version, stream, sigkey, enkey, addr=None, ripe=None,
avg_pow=None, extra_bytes=None, bitfield=None
):
self.version = version
self.stream = stream
self.sigkey = sigkey
self.enkey = enkey
if bitfield:
self.bitfield = bitfield
if ripe is None:
if addr:
status, dversion, dstream, ripe = decodeAddress(addr)
assert status == 'success'
assert version == dversion
assert stream == dstream
else:
_sha = hashlib.new('sha512')
_sha.update(sigkey + enkey)
ripe = RIPEMD160Hash(_sha.digest()).digest()
self.ripe = ripe
if avg_pow:
self.avg_pow = avg_pow
if extra_bytes:
self.extra_bytes = extra_bytes
self.addr = addr or encodeAddress(self.version, self.stream, self.ripe)
def encrypt(self, data):
return highlevelcrypto.encrypt(data, b'04' + hexlify(self.enkey))
class Identity(Address):
priv_sigkey = None
priv_enkey = None
@classmethod
def fromWIF(cls, sigkey, enkey, bitfield=pack('>I', 0)):
return cls(
hexlify(shared.decodeWalletImportFormat(sigkey)),
hexlify(shared.decodeWalletImportFormat(enkey)), bitfield=bitfield)
@classmethod
def fromConfig(cls, addr, config):
sigkey_base58 = config.get(addr, 'privsigningkey')
enkey_base58 = config.get(addr, 'privencryptionkey')
bitfield = 0
# send ack
if not config.safeGetBoolean(addr, 'dontsendack'):
bitfield |= protocol.BITFIELD_DOESACK
return cls.fromWIF(
sigkey_base58, enkey_base58, bitfield=pack('>I', bitfield))
def __init__(
self, sigkey, enkey, addr=None, bitfield=None,
version=None, stream=None, ripe=None,
avg_pow=None, extra_bytes=None
):
self.priv_sigkey = sigkey
self.priv_enkey = enkey
if version:
self.version = version
if stream:
self.stream = stream
sigkey = unhexlify(highlevelcrypto.privToPub(sigkey))[1:]
enkey = unhexlify(highlevelcrypto.privToPub(enkey))[1:]
super(Identity, self).__init__(
self.version, self.stream, sigkey, enkey,
addr, ripe, avg_pow, extra_bytes)
def sign(self, data):
return highlevelcrypto.sign(data, self.priv_sigkey)
def decrypt(self):
pass
def assemble_msg(
fromid, toaddr, subject, message,
friend=False, ttl=86400, encoding=2
):
if ttl > 28 * 24 * 60 * 60:
ttl = 28 * 24 * 60 * 60
# add some randomness to the TTL
ttl = int(ttl + random.randrange(-300, 300))
embedded_time = int(time.time() + ttl)
payload = encodeVarint(fromid.version)
payload += encodeVarint(fromid.stream)
payload += fromid.bitfield
payload += fromid.sigkey + fromid.enkey
if fromid.version >= 3:
payload += encodeVarint(
defaults.networkDefaultProofOfWorkNonceTrialsPerByte if friend
else fromid.avg_pow)
payload += encodeVarint(
defaults.networkDefaultPayloadLengthExtraBytes if friend
else fromid.extra_bytes)
payload += toaddr.ripe
payload += encodeVarint(encoding)
encoded_message = helper_msgcoding.MsgEncode(
{"subject": subject, "body": message}, encoding)
payload += encodeVarint(encoded_message.length)
payload += encoded_message.data
if not protocol.checkBitfield(
toaddr.bitfield, protocol.BITFIELD_DOESACK
):
logger.info(
'Not bothering to include ackdata because'
' the receiver said that they won\'t relay it anyway.'
)
ack_payload = ''
else:
if ttl < 24 * 60 * 60:
ttl = 24 * 60 * 60 # 1 day
elif ttl < 7 * 24 * 60 * 60:
ttl = 7 * 24 * 60 * 60 # 1 week
else:
ttl = 28 * 24 * 60 * 60 # 4 weeks
ttl = int(ttl + random.randrange(-300, 300))
ack_payload = pack('>Q', int(time.time() + ttl)) + \
genAckPayload(toaddr.stream)
ack_payload = protocol.CreatePacket('object', ack_payload)
payload += encodeVarint(len(ack_payload))
payload += ack_payload
logger.info('Payload: %s', hexlify(payload))
header_tmp = pack('>Q', embedded_time) + '\x00\x00\x00\x02' + \
encodeVarint(1) + encodeVarint(toaddr.stream)
signature = fromid.sign(header_tmp + payload)
payload += encodeVarint(len(signature))
payload += signature
encrypted = toaddr.encrypt(payload)
encrypted_payload = header_tmp + encrypted
return encrypted_payload
if __name__ == "__main__":
fromid = Identity(
'93d0b61371a54b53df143b954035d612f8efa8a3ed1cf842c2186bfd8f876665',
'4b0b73a54e19b059dc274ab69df095fe699f43b17397bca26fdf40f4d7400a3a',
'BM-onkVu1KKL2UaUss5Upg9vXmqd3esTmV79', version=2)
# TODO: generate -> [chan] test
toaddr = Identity.fromWIF(
'5JY1CFeeyN4eyfL35guWAuUqu5VLmd7LojtkNP6wmt5msZxxZ57',
'5J1oDgZDicNhUgbfzBDQqi2m5jUPnDrfZinnTqEEEaLv63jVFTM')
print(hexlify(
assemble_msg(
fromid, toaddr,
'hello', 'The quick brown fox jumps over the lazy dog.', True)
))