Add an example script on assembling a message
This commit is contained in:
parent
1db96e2e27
commit
43ba487d67
221
assemble_msg.py
Normal file
221
assemble_msg.py
Normal 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)
|
||||
))
|
Loading…
Reference in New Issue
Block a user