Example #1
0
File: a2mxc.py Project: p1tt/A2MX
			data = data[7:]
			got_len = True
		if len(data) < l:
			continue
		assert l > 4
		assert len(data) == l
		got_rid = data[:4]
		assert got_rid == rid
		bs = BSON.decode(bytes(data[4:]), tz_aware=True)
		if 'error' in bs:
			raise RemoteException(bs['error'])
		if len(bs) == 1 and 'data' in bs:
			return bs['data']
		return bs

r = send({'access': ecc.pubkeyData()})
rauth = r['auth']
pubkey = r['pubkey']
remote_ecc = ECC(pubkey_data=pubkey)

sigdata = BSON.encode({ 'auth': rauth })
sig = ecc.signAddress(sigdata)
auth = send({'sig': sig})
assert 'sig' in auth
peer_verified = remote_ecc.verifyAddress(auth['sig'], sigdata)
assert peer_verified == True

#docs = send(request('find', { 'timestamp': { '$gt': datetime.datetime.min }}, {}))
#for doc in docs:
#	print(send(request('find', doc, None)))
paths = send(request('paths'))
Example #2
0
class A2MXPath():
	def __init__(self, A=None, B=None, T=None, UA=None, UB=None, M=None, PB=None, PF=None, PD=None, P=None, SA=None, SB=None, D=None, DS=None):
		# A = node A public key data (address, sign and encrypt compressed public keys)
		# B = node B public key data
		# T = timestamp
		# UA = AX URI node A
		# UB = AX URI node B
		# M = MaxSize
		# PB = POW broadcast
		# PF = POW forward
		# PD = POW direct
		# P = path proof of work
		# SA = node A signature (over A, B, T and UA if present)
		# SB = node B signature (over A, B, T and UB if present)
		# D = deleted timestamp
		# DS = deleted signature (over A, B, T, SA, SB and D)
		# A must always be the smaller binary value

		if not isinstance(A, ECC):
			self.__a = ECC(pubkey_data=A)
		else:
			self.__a = A
			if not SA and self.__a.hasPrivkey() and not PB < 0:
				UA = config['publish_axuri']

		if not isinstance(B, ECC):
			self.__b = ECC(pubkey_data=B)
		else:
			self.__b = B
			if not SB and self.__b.hasPrivkey() and not PB < 0:
				UB = config['publish_axuri']

		def testURI(uri):
			if uri == None:
				return
			if len(uri) > 32:
				raise ValueError('URI too long')
			try:
				host, port = uri.split(':')
			except ValueError:
				raise ValueError('Invalid URI')
			try:
				int(port)
			except ValueError:
				raise ValueError('Invalid URI')
			validChars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.0123456789'
			if not all(c in validChars for c in host):
				raise ValueError('Invalid chars in URI')
		testURI(UA)
		testURI(UB)

		self.__ua = UA
		self.__ub = UB

		if self.__a.pubkeyData() > self.__b.pubkeyData():
			self.__a, self.__b = self.__b, self.__a
			self.__ua, self.__ub = self.__ub, self.__ua

		if T:
			if T > datetime.datetime.now(datetime.timezone.utc):
				raise ValueError('timestamp is in the future')
			self.__t = T
		else:
			self.__t = now()

		assert isinstance(M, int)
		assert isinstance(PB, float)
		assert isinstance(PF, float)
		assert isinstance(PD, float)
		self.__maxsize = M
		self.__pb = PB
		self.__pf = PF
		self.__pd = PD

		self.__sigod = OrderedDict()
		self.__sigod['A'] = self.__a.pubkeyData()
		self.__sigod['B'] = self.__b.pubkeyData()
		self.__sigod['T'] = self.__t
		self.__sigod['M'] = self.__maxsize
		self.__sigod['PB'] = PB
		self.__sigod['PF'] = PF
		self.__sigod['PD'] = PD

		if self.__ua:
			self.__sigod['UA'] = self.__ua
			sigdata_a = BSON.encode(self.__sigod)
			del self.__sigod['UA']
		else:
			sigdata_a = BSON.encode(self.__sigod)

		if self.__ub:
			self.__sigod['UB'] = self.__ub
			sigdata_b = BSON.encode(self.__sigod)
			del self.__sigod['UB']
		else:
			sigdata_b = BSON.encode(self.__sigod)

		self.__sa = SA
		if SA == None:
			if self.__a.hasPrivkey():
				self.__sa = self.__a.signAddress(sigdata_a)
		else:
			verify = self.__a.verifyAddress(SA, sigdata_a)
			if not verify:
				raise InvalidDataException('SA signature verify failed.')

		self.__sb = SB
		if SB == None:
			if self.__b.hasPrivkey():
				self.__sb = self.__b.signAddress(sigdata_b)
		else:
			verify = self.__b.verifyAddress(SB, sigdata_b)
			if not verify:
				raise InvalidDataException('SA signature verify failed.')

		if not (self.__sa or self.__sb):
			raise ValueError('Invalid signatures.')

		pow_data = BSON.encode(self.__sigod)
		if P == True:
			self.pow_done = None
			def setpow(nonce):
				self.__pow = nonce
				if self.pow_done:
					self.pow_done()
			calculatePOW(message=pow_data, difficulty=3.0, callback=setpow)
		else:
			self.__pow = P
			if isinstance(P, int):
				if not checkPOW(message=pow_data, difficulty=3.0, nonce=P):
					raise ValueError('Invalid POW')
			elif P != None:
				raise ValueError('Invalid POW value')

		self.__d = D
		self.__ds = DS

		if self.__d:
			if self.__d > datetime.datetime.now(datetime.timezone.utc):
				raise ValueError('Deleted timestamp is in the future.')
			if self.__d < self.__t:
				raise ValueError('Deleted timestamp is older than timestamp.')
			if not self.isComplete:
				raise ValueError('Deleted path may not be incomplete.')

			self.__sigod['SA'] = self.__sa
			self.__sigod['SB'] = self.__sb
			self.__sigod['D'] = self.__d
			sigdata = BSON.encode(self.__sigod)
			verify = self.__a.verifyAddress(self.__ds, sigdata) or self.__b.verifyAddress(self.__ds, sigdata)
			if not verify:
				raise InvalidDataException('DS signature verify failed.')

		self.__hash = hash(self.AHash + self.BHash)
		self.__longhash = hashlib.sha256(BSON.encode(self.__sigod)).digest()

	def __getstate__(self):
		state = { 'A': self.__a.pubkeyData(), 'B': self.__b.pubkeyData(), 'T': self.__t, 'SA': self.__sa, 'SB': self.__sb, 'M': self.__maxsize, 'PB': self.__pb, 'PF': self.__pf, 'PD': self.__pd }
		if self.__ua:
			state['UA'] = self.__ua
		if self.__ub:
			state['UB'] = self.__ub
		if self.__pow:
			state['P'] = self.__pow
		if self.__d:
			state['D'] = self.__d
			state['DS'] = self.__ds
		return state

	def __setstate__(self, state):
		return A2MXPath.__init__(self, **state)

	def __hash__(self):
		return self.__hash

	@property
	def longHash(self):
		return self.__longhash

	@property
	def isComplete(self):
		return self.__sa != None and self.__sb != None and self.__pow != None

	@property
	def data(self):
		return self.__getstate__()

	@property
	def A(self):
		return self.__a.pubkeyData()
	@property
	def AHash(self):
		return self.__a.pubkeyHash()
	@property
	def AURI(self):
		return self.__ua

	@property
	def B(self):
		return self.__b.pubkeyData()
	@property
	def BHash(self):
		return self.__b.pubkeyHash()
	@property
	def BURI(self):
		return self.__ub

	@property
	def deleted(self):
		return self.__d

	@property
	def timestamp(self):
		return self.__t
	@property
	def newest_timestamp(self):
		return self.__d or self.__t

	def markdelete(self):
		assert self.__d == None
		assert 'SA' not in self.__sigod
		assert 'SB' not in self.__sigod
		assert 'D' not in self.__sigod

		self.__sigod['SA'] = self.__sa
		self.__sigod['SB'] = self.__sb
		self.__d = now()
		self.__sigod['D'] = self.__d
		sigdata = BSON.encode(self.__sigod)
		if self.__a.hasPrivkey():
			self.__ds = self.__a.signAddress(sigdata)
		elif self.__b.hasPrivkey():
			self.__ds = self.__b.signAddress(sigdata)
		else:
			raise ValueError('Cannot mark path as deleted without private key.')

	def __eq__(self, other):
		if not isinstance(other, A2MXPath):
			return False
		return self.A == other.A and self.B == other.B

	def equal(self, other):
		if not isinstance(other, A2MXPath):
			return False
		return self.data == other.data

	def otherHash(self, otherHash):
		if otherHash == self.AHash:
			return self.BHash
		elif otherHash == self.BHash:
			return self.AHash
		raise ValueError('otherHash is neither A or B.')

	def ecc(self, h):
		if h == self.AHash:
			return self.A
		if h == self.BHash:
			return self.B
		raise ValueError("Hash is neither A nor B")

	@property
	def hashes(self):
		return (self.AHash, self.BHash)

	def is_better_than(self, other):
		if self != other:
			raise ValueError('Cannot compare paths with different nodes')
		return self.newest_timestamp > other.newest_timestamp

	def __str__(self):
		return 'A: {}{} B: {}{} Timestamp: {} M: {} PB: {} PF: {} PD: {} POW: {} Deleted: {}{}'.format(
			self.__a.pubkeyHashBase58(), " ({})".format(self.__ua) if self.__ua else "",
			self.__b.pubkeyHashBase58(), " ({})".format(self.__ub) if self.__ub else "",
			self.__t.isoformat(),
			self.__maxsize, self.__pb, self.__pf, self.__pd, self.__pow,
			self.__d.isoformat() if self.__d else False,
			"" if self.isComplete else " Incomplete")