def _deserialize_header_v2(self, cmsg): if len(cmsg) < _header_size_b64_v2: return False binmsg = b64decode(cmsg[:_header_size_b64_v2]) if binmsg[0:4] != _msg_api_ver_v2: return False hexmsg = hexlify(binmsg) self.time = int(hexmsg[8:16], 16) self.expire = int(hexmsg[16:24], 16) if not (hexmsg[24:26] == b'02' or hexmsg[24:26] == b'03'): return False if not (hexmsg[90:92] == b'02' or hexmsg[90:92] == b'03'): return False if not (hexmsg[156:158] == b'02' or hexmsg[156:158] == b'03'): return False self.I = Point.decompress(hexmsg[24:90]) self.J = Point.decompress(hexmsg[90:156]) self.K = Point.decompress(hexmsg[156:222]) self.blocklen = int(hexmsg[222:230], 16) self.reserved = int(hexmsg[230:246], 16) # print('deserialize blocklen = ' + str(self.blocklen)) if len(cmsg) >= _header_size_w_sig_b64_v2: sig_b64 = cmsg[_header_size_b64_v2:_header_size_w_sig_b64_v2] sighex = hexlify(b64decode(sig_b64)) self.sig = (int(sighex[0:64], 16), int(sighex[64:128], 16)) self.nonce = int(sighex[128:138], 16) return True
def __init__(self, name=None): self.P = Point(0,0) self.version = _format_version_v2 self.addr = {'mask': 0, 'mtgt': 0} self.t0 = 0 self.ts = 0 self.Tbk = ({'otp' : 0, 'T' : Point(0,0)}) self.name = name self.metadata = {} self.initialized = False self.last_steps = None self.last_pubkey_point = None
def refresh(self): req = HTTPRequest(self._baseurl() + _statusPath, method='GET') r = CTClient._sclient.fetch(req) if r.code != 200: return False pub = json.loads(r.body.decode('UTF-8'))['pubkey'] self.Pkey = Point.decompress(pub.encode('UTF-8')) return True
def deserialize(ikey): if isinstance(ikey, str): ikey = ikey.encode() # verify checksum inp = ikey.split(b':C') if len(inp) != 2: print('f1') return None ckck = hashlib.sha256(inp[0]).hexdigest()[-8:].encode().upper() if ckck != inp[1]: print('f2') return None # verify keys inp = inp[0].split(b':') if len(inp) < 7: print('f3') return None if ((inp[0][:1] != b'P') or (inp[1][:1] != b'K') or (inp[2][:1] != b'M') or (inp[3][:1] != b'N') or (inp[4][:1] != b'Z') or (inp[5][:1] != b'S') or (inp[6][:1] != b'R')): print('f4') return None # verify version if (inp[0][1:] != b'0200'): print('f5', inp[0]) return None # decompress point z = PublicKey() z.P = Point.decompress(inp[1][1:]) # z.addr['mask'] = int(inp[2][1:], 16) z.addr['mtgt'] = int(inp[3][1:], 16) z.t0 = int(inp[4][1:], 16) z.ts = int(inp[5][1:], 16) # time base key(s) ntbk = int(inp[6][1:]) Tbk = [] for i in range(ntbk): key = {} key['otp'] = int(inp[7 + (2 * i)][1:], 16) key['T'] = Point.decompress(inp[8 + (2 * i)][1:]) Tbk.append(key) z.Tbk = Tbk z.initialized = True return z
def loadjson(load): try: raw = json.loads(load) expire = raw['expire'] pubkey = Point.decompress(raw['pubkey']) signature = raw['signature'] return NAK(expire, pubkey, signature) except: return None
def _deserialize_header_v1(self, cmsg): if len(cmsg) < _header_size_v1: return False hdrdata = cmsg[:_header_size_v1].split(b':') if len(hdrdata) != 6: return False if hdrdata[0] != _msg_api_ver_v1: return False self.time = int(hdrdata[1], 16) self.expire = int(hdrdata[2], 16) self.I = Point.decompress(hdrdata[3]) self.J = Point.decompress(hdrdata[4]) self.K = Point.decompress(hdrdata[5]) self.version = "0100" if len(cmsg) >= _header_size_w_sig_v1: hdrdata = cmsg[:_header_size_w_sig_v1].split(b':') if len(hdrdata) != 8: return False self.sig = (int(hdrdata[6], 16), int(hdrdata[7], 16)) return True
def loadjson(msgjson): try: mdict = json.loads(msgjson) mnew = MessageFile() mnew.filepath = mdict['filepath'] mnew.time = mdict['time'] mnew.expire = mdict['expire'] mnew.I = Point.decompress(mdict['I']) mnew.J = Point.decompress(mdict['J']) mnew.K = Point.decompress(mdict['K']) mnew.size = mdict['size'] mnew.time_str = mdict['time_str'] mnew.expire_str = mdict['expire_str'] mnew.servertime = mdict['servertime'] mnew.sig = mdict['signature'] mnew.header = mdict['header'] mnew.version = mdict['version'] return mnew except: return None
def uniformMap(self, n): """UniformMap maps values from Fq to E(Fq) in a uniform manner by elliptic curve point multiplication. While this does produce a uniform mapping within the ring of the generator point, using this map exposes the discrete logarithm of the resultant point (as log.G = n). """ if (n != int(n)) or (n < 0): raise ValueError("Invalid Input") if n == 0: return Point(infinity=True, curve=self.curve) # just to be sure, force x to be a member of Fq u = int(n) % self.q return (self.G * u)
def deserialize(rawbytes): etime = int(hexlify(rawbytes[0:4]), 16) #print('time = ' + str(time.gmtime(etime))) Pkey = Point.decompress(hexlify(rawbytes[4:37])) #print('point = ' + Pkey.compress()) sig0 = int(hexlify(rawbytes[37:69]),16) sig1 = int(hexlify(rawbytes[69:101]),16) sig = (sig0, sig1) #print('sig = (0x%032x, 0x%032x)' % (sig[0], sig[1])) #print('verifying %s' % hexlify(rawbytes[:37])) if not NAK.ecdsa.verify(Pkey,sig,rawbytes[:37]): #print('verify failed') return None return NAK(etime, Pkey, sig)
def deterministicMap(self, n): """Using the original algorithm proposed by Thomas Icart, calculates a point on E(Fq) assuming n is a member of Fq. H(0) is mapped to O (point at infinity). Points are calculated in affine coordinates and returned as a Point Object. Note: deterministicMap reliably maps Fq to E(Fq), but as not all points on the curve can be parameterized, the results are not uniform and the distribution is differentiable from a collection of random points """ if (n != int(n)) or (n < 0): raise ValueError("Invalid Input") if n == 0: return Point(infinity=True, curve=self.curve) # just to be sure, force x to be a member of Fq u = int(n) % self.q # print("u = ", u) u6_inv = pow((6 * u) % self.q, self.q - 2, self.q) assert ((6 * u * u6_inv) % self.q) == 1 v = ((self._3a - pow(u, 4, self.q)) * u6_inv) % self.q u_6 = pow(u, 6, self.q) # print ("u_6 =", u_6) # print ("27_inv =", self._27inv) u_6o27 = (pow(u, 6, self.q) * self._27inv) % self.q assert ((u_6o27 * 27) % self.q) == u_6 foo = ((pow(v, 2, self.q) - self.b) - u_6o27) % self.q # print ("foo = ", foo) curootfoo = self._cubeRoot(foo) # print ("curootfoo = ", curootfoo) assert (pow(curootfoo, 3, self.q) == foo) u_2 = pow(u, 2, self.q) u_2o3 = (pow(u, 2, self.q) * self._3inv) % self.q assert ((u_2o3 * 3) % self.q) == u_2 x = (curootfoo + u_2o3) % self.q y = ((u * x) + v) % self.q return Point(x, y, infinity=False, curve=self.curve)
def refresh(self): r = None rtime = time.time() logging.info('refresh called for ' + self._baseurl()) req = HTTPRequest(self._baseurl() + _statusPath, method='GET', connect_timeout=30, request_timeout=60) try: r = sclient.fetch(req) except: self.fails += 1 return False if r.code != 200: self.fails += 1 return False status = json.loads(r.body.decode('UTF-8')) if 'pubkey' in status: self.Pkey = Point.decompress(status['pubkey'].encode('UTF-8')) if 'coinhost' in status: self.coinhost = status['coinhost'] if 'coinport' in status: self.coinport = status['coinport'] self.lastseen = rtime req = HTTPRequest(self._baseurl() + _peerListPath, method='GET', connect_timeout=30, request_timeout=60) try: r = sclient.fetch(req) except (HTTPError, ConnectionRefusedError, TimeoutError): self.fails += 1 return False if r.code != 200: self.fails += 1 return False rlist = json.loads(r.body.decode('UTF-8')) self.peerlist = [] for r in rlist: n = PeerListItem.loaddict(r) if n is not None: self.add_peer(n) self.peerlist.sort() self.fails = 0 return True
def loadjson(j): try: j = json.loads(j) n = PeerHost(j['host'], int(j['port'])) if 'pubkey' in j: n.Pkey = Point.decompress(j['pubkey']) n.peerlist = [] for li in j['peerlist']: nli = PeerListItem.loaddict(li) if nli is not None: n.peerlist.append(nli) if 'coinhost' in j: n.coinhost = j['coinhost'] if 'coinport' in j: n.coinhost = j['coinport'] return n except: return None
def refresh(self): r = None rtime = time.time() logging.info('refresh called for ' + self._baseurl()) req = HTTPRequest(self._baseurl() + _statusPath, method='GET', connect_timeout=30, request_timeout=60) try: r = sclient.fetch(req) except: self.fails += 1 return False if r.code != 200: self.fails += 1 return False status = json.loads(r.body.decode('UTF-8')) if 'pubkey' in status: self.Pkey = Point.decompress(status['pubkey'].encode('UTF-8')) if 'coinhost' in status: self.coinhost = status['coinhost'] if 'coinport' in status: self.coinport = status['coinport'] self.lastseen = rtime req = HTTPRequest(self._baseurl() + _peerListPath, method='GET', connect_timeout=30, request_timeout=60) try: r = sclient.fetch(req) except (HTTPError, ConnectionRefusedError, TimeoutError) : self.fails += 1 return False if r.code != 200: self.fails += 1 return False rlist = json.loads(r.body.decode('UTF-8')) self.peerlist = [] for r in rlist: n = PeerListItem.loaddict(r) if n is not None: self.add_peer(n) self.peerlist.sort() self.fails = 0 return True
def post(self, pubkey=None): if pubkey is None: self.set_status(400) self.finish() #logging.info('onion: received request') if len(pubkey) != 66: self.set_status(400) self.finish() #logging.info('onion: validated keylength') try: P = Point.decompress(pubkey) except: self.set_status(400) self.finish() ecdh = P * server_p keybin = hashlib.sha256(ecdh.compress()).digest() b = self.request.body bd = base64.b64decode(b) nakpubraw = hexlify(bd[:33]) if not opts['standalone']: nakpub = ncache.get_nak(nakpubraw) if nakpub is None: self.set_status(400) self.finish() sig = (int(hexlify(bd[33:65]),16), int(hexlify(bd[65:97]),16)) if not nakpub.verify(sig,bd[97:]): self.set_status(400) self.finish() logging.info('validated access for NAK %s' % nakpubraw) ivcount = int(hexlify(bd[97:113]),16) counter = Counter.new(128,initial_value=ivcount) cryptor = AES.new(keybin, AES.MODE_CTR, counter=counter) plaintext = cryptor.decrypt(bd[113:]) plaintext = plaintext.decode('UTF-8') #logging.info('onion received: ' + plaintext) o_r = json.loads(plaintext) if o_r['local'] is True: if o_r['action'].lower() == 'get': o_server = 'http://127.0.0.1:' + str(opts['listenport']) + '/' o_path = o_r['url'] self.client_R = Point.decompress(o_r['replykey']) req = tornado.httpclient.HTTPRequest(o_server+o_path, method='GET', headers=o_r['headers'], connect_timeout=30, request_timeout=60) onion_client.fetch(req, self.callback_encrypt) elif o_r['action'].lower() == 'post': o_server = 'http://127.0.0.1:' + str(opts['listenport']) + '/' o_path = o_r['url'] self.client_R = Point.decompress(o_r['replykey']) req = tornado.httpclient.HTTPRequest(o_server+o_path, method='POST', body=o_r['body'], headers=o_r['headers'], connect_timeout=30, request_timeout=60) onion_client.fetch(req, self.callback_encrypt) else: if nak is None: self.set_status(400) self.finish() if (len(o_r['pubkey']) != 66) or not((o_r['pubkey'][:2] == '02') or (o_r['pubkey'][:2] == '03')): self.set_status(400) self.finish() try: i = int(o_r['pubkey'],16) except: self.set_status(400) self.finish() o_server = 'http://' + o_r['host'] + ':' + str(o_r['port']) + '/' o_path = 'onion/' + o_r['pubkey'] o_raw = base64.b64decode(o_r['body']) sig = nak.sign(o_raw) o_body = base64.b64encode(nakpubbin + unhexlify('%064x' % sig[0]) + unhexlify('%064x' % sig[1]) + o_raw) req = tornado.httpclient.HTTPRequest(o_server+o_path, method='POST', body=o_body, connect_timeout=30, request_timeout=60) onion_client.fetch(req, self.callback)
import requests import hashlib import base64 import json import sys from binascii import hexlify, unhexlify import nak from ecpy.curves import curve_secp256k1 from ecpy.point import Point, Generator from ecpy.ecdsa import ECDSA from Crypto.Random import random from Crypto.Cipher import AES from Crypto.Util import Counter Point.set_curve(curve_secp256k1) _G = Generator.init(curve_secp256k1['G'][0], curve_secp256k1['G'][1]) ECDSA.set_generator(_G) client_p = random.randint(1,curve_secp256k1['n']-1) client_P = _G * client_p client_r = random.randint(1,curve_secp256k1['n']-1) client_R = _G * client_r ecdsa=ECDSA() _server = "http://indigo.bounceme.net:5000/" _server2 = "http://coopr8.com:5000/" _server3 = "http://ciphrtxt.com:5000/" _status = "api/status/"
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from Crypto.Random import random from binascii import hexlify, unhexlify import time import json from ecpy.curves import curve_secp256k1 from ecpy.point import Point, Generator from ecpy.ecdsa import ECDSA _def_curve = curve_secp256k1 Point.set_curve(_def_curve) ECDSA.set_curve(_def_curve) ECDSA.set_generator(Generator.init(_def_curve['G'][0], _def_curve['G'][1])) class NAK(object): n = _def_curve['n'] G = Generator.init(_def_curve['G'][0], _def_curve['G'][1]) ecdsa = ECDSA() def __init__(self, expire=None, pubkey=None, signature=None, privkey=None): self.expire = expire self.pubkey = pubkey self.signature = signature self.privkey = privkey if privkey is not None and pubkey is None: self.pubkey = NAK.G * privkey
iH2 = IcartHash.deserialize(iH.serialize()) print("iH(0) = ", h0) for i in range(0, q): # n = random.randint(0,q-1) n = i hn = iH.hashval(n) assert hn == iH2.hashval(n) ncollisions = 0 coll = [] for j in range(0, q): hc = iH.hashval(j) if (i != j) and (hc == hn): ncollisions += 1 # print("collision i, j, hash = ", i, j, hc) coll.append(j) hp = Point(infinity=True, curve=curve) if hn[1] == False: hp = Point.decompress(hn[0]) ha = hp.affine() han = (ha[0], ha[1], hp.is_infinite) if ncollisions > 0: print("n, iH(n) = ", n, han, ":", ncollisions, "collisions", coll) else: print("n, iH(n) = ", n, han) x,y = han[0], han[1] R = Point(x, y, infinity=han[2]) assert R.is_valid() Q = R + G assert Q.is_valid() rt = (pow(x, 3, q) + (a * x) + b) % q lf = pow(y, 2, q)
import requests import hashlib import base64 import json import sys from binascii import hexlify, unhexlify import nak from ecpy.curves import curve_secp256k1 from ecpy.point import Point, Generator from ecpy.ecdsa import ECDSA from Crypto.Random import random from Crypto.Cipher import AES from Crypto.Util import Counter Point.set_curve(curve_secp256k1) _G = Generator.init(curve_secp256k1['G'][0], curve_secp256k1['G'][1]) ECDSA.set_generator(_G) client_p = random.randint(1, curve_secp256k1['n'] - 1) client_P = _G * client_p client_r = random.randint(1, curve_secp256k1['n'] - 1) client_R = _G * client_r ecdsa = ECDSA() _server = "http://indigo.bounceme.net:5000/" _server2 = "http://coopr8.com:5000/" _server3 = "http://ciphrtxt.com:5000/" _status = "api/status/"
return from_jacobian(jacobian_add(to_jacobian(a), to_jacobian(b))) if __name__ == '__main__': p = _curve['p'] if False: for x in range(min(512,p)): xi = inv(x, p) test = (x*xi) % p #print x, xi, x*xi, test if x != 0: assert test == 1 print('Setting up Generators') G = _curve['G'] print('ref Gen ' + str(G)) Gpt = Point(G[0],G[1]) print('Point Gen ' + str(Gpt)) GenG = Generator.init(G[0],G[1]) print('Generator Gen ' + str(GenG)) GenG2 = Generator.init(G[0],G[1]) print('Generator Gen ' + str(GenG2)) assert GenG2 is GenG assert Gpt.is_valid() assert GenG.is_valid() print('Generator uncompressed ' + str(GenG.uncompressed_format())) InfP = Point() ex = InfP.compress() exraw = InfP.uncompressed_format() InfP2 = Point.decompress(ex) InfP3 = Point.decompress(exraw) InfP4 = Point.decompress(ex.decode())
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from ecpy.point import Point, Generator import ecpy.curves as curves from Crypto.Random import random from Crypto.Hash import RIPEMD from hashlib import sha256 import hashlib from binascii import hexlify, unhexlify from base58 import b58encode, b58decode # set up elliptic curve environment _C = curves.curve_secp256k1 Point.set_curve(_C) _G = Generator.init(_C['G'][0], _C['G'][1]) _network_id = { 'ct-indigo': { 'pub': b'1c', 'priv': b'bb' }, 'ct-red': { 'pub': b'50', 'priv': b'a3' }, 'bt-main': { 'pub': b'00', 'priv': b'80' }, 'bt-test': { 'pub': b'6f', 'priv': b'ef' }, 'bt-simtest': { 'pub': b'3f', 'priv': b'64' }, } _pfmt = '%%0%dx' % (((_C['bits'] + 7) >> 3) << 1) _default_network = _network_id['ct-indigo'] class WalletPubkey (object):
from binascii import hexlify, unhexlify import json import logging import time import os import random from tornado.httpclient import HTTPClient, HTTPRequest, HTTPError from lbr import lbr from msgstoreclient import MsgStore import ctcoin.rpc from ecpy.curves import curve_secp256k1 from ecpy.point import Point _curve = curve_secp256k1 Point.set_curve(_curve) _default_port = 7754 _default_coin_port = 7764 _default_max_age = (4 * 60 * 60) # 4 hours _default_max_fails = 1 _statusPath = 'api/v2/status/' _peerListPath = 'api/v2/peers/' _peerUpdatePath = 'api/v2/peers/' _peer_msync_timeout = 10 _peer_psync_interval = (2 * 60) # 2 minutes sclient = HTTPClient()
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from ecpy.point import Point, Generator import ecpy.curves as curves from Crypto.Random import random from Crypto.Hash import RIPEMD from hashlib import sha256 import hashlib from binascii import hexlify, unhexlify from base58 import b58encode, b58decode # set up elliptic curve environment _C = curves.curve_secp256k1 Point.set_curve(_C) _G = Generator.init(_C['G'][0], _C['G'][1]) _network_id = { 'ct-indigo': { 'pub': b'1c', 'priv': b'bb' }, 'ct-red': { 'pub': b'50', 'priv': b'a3' }, 'bt-main': { 'pub': b'00', 'priv': b'80' },
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. from ecpy.point import Point, Generator import ecpy.curves as curves from Crypto.Random import random from Crypto.Hash import RIPEMD from hashlib import sha256 import hashlib from binascii import hexlify, unhexlify from base58 import b58encode, b58decode # set up elliptic curve environment c = curves.curve_secp256k1 Point.set_curve(c) G = Generator(c['G'][0], c['G'][1]) #mainnet pub_prefix = '00' prv_prefix = '80' #testnet pub_prefix = '6f' prv_prefix = 'ef' #simtest pub_prefix = '3f' prv_prefix = '64' #ctindigonet pub_prefix = '1c' prv_prefix = 'bb' #ctrednet
from binascii import hexlify, unhexlify import json import logging import time import os import random from tornado.httpclient import HTTPClient, HTTPRequest, HTTPError from lbr import lbr from msgstoreclient import MsgStore import ctcoin.rpc from ecpy.curves import curve_secp256k1 from ecpy.point import Point _curve = curve_secp256k1 Point.set_curve(_curve) _default_port=7754 _default_coin_port=7764 _default_max_age=(4*60*60) # 4 hours _default_max_fails=1 _statusPath = 'api/v2/status/' _peerListPath = 'api/v2/peers/' _peerUpdatePath = 'api/v2/peers/' _peer_msync_timeout = 10 _peer_psync_interval = (2*60) # 2 minutes sclient = HTTPClient()
class PublicKey (object): def __init__(self, name=None): self.P = Point(0,0) self.version = _format_version_v2 self.addr = {'mask': 0, 'mtgt': 0} self.t0 = 0 self.ts = 0 self.Tbk = ({'otp' : 0, 'T' : Point(0,0)}) self.name = name self.metadata = {} self.initialized = False self.last_steps = None self.last_pubkey_point = None def set_metadata(self, metakey, metavalue): self.metadata[metakey] = metavalue def get_metadata(self, metakey): if metakey not in self.metadata: return None return self.metadata[metakey] def label(self): txt = str(self.P)[:8] if self.name: txt = self.name + '_' + txt return txt def current_pubkey_point(self, timeval=None): """Calulates the current EC public key point as a pseudorandom linear combination of the primary P key and multiple time-based T keys using an algorithm based on HOTP/TOTP""" if not self.initialized: return None if timeval is None: timeval = int(time.time()) steps = (timeval - self.t0) / self.ts if steps == self.last_steps: return self.last_pubkey_point P = self.P for i in range(len(self.Tbk)): okeyt = (_pfmt % (self.Tbk[i]['otp'])).encode() stepsd = ('%07d' % (steps % 10000000)).encode() otphmac = hmac.new(okeyt, stepsd, hashlib.sha256) hashv = otphmac.hexdigest() hashi = int(hashv, 16) % _C['p'] S = (self.Tbk[i]['T']) * hashi P = S + P self.last_steps = steps self.last_pubkey_point = P return P def serialize_pubkey(self): ekey = ('P%04x' % self.version).encode() ekey += b':K' + self.P.compress().upper() ekey += b':M' + (_Mfmt % self.addr['mask']).encode() ekey += b':N' + (_Mfmt % self.addr['mtgt']).encode() ekey += b':Z' + ('%08X' % self.t0).encode() ekey += b':S' + ('%08X' % self.ts).encode() ekey += b':R' + ('%04X' % len(self.Tbk)).encode() for Tbk in self.Tbk: ekey += b':F' + (_Pfmt % Tbk['otp']).encode() ekey += b':T' + Tbk['T'].compress().upper() ekey += b':C' + (hashlib.sha256(ekey).hexdigest()[-8:]).encode().upper() return ekey def serialize(self): return self.serialize_pubkey() @staticmethod def deserialize(ikey): if isinstance(ikey, str): ikey = ikey.encode() # verify checksum inp = ikey.split(b':C') if len(inp) != 2: print('f1') return None ckck = hashlib.sha256(inp[0]).hexdigest()[-8:].encode().upper() if ckck != inp[1]: print('f2') return None # verify keys inp = inp[0].split(b':') if len(inp) < 7: print('f3') return None if ((inp[0][:1] != b'P') or (inp[1][:1] != b'K') or (inp[2][:1] != b'M') or (inp[3][:1] != b'N') or (inp[4][:1] != b'Z') or (inp[5][:1] != b'S') or (inp[6][:1] != b'R')): print('f4') return None # verify version if (inp[0][1:] != b'0200'): print('f5', inp[0]) return None # decompress point z = PublicKey() z.P = Point.decompress(inp[1][1:]) # z.addr['mask'] = int(inp[2][1:], 16) z.addr['mtgt'] = int(inp[3][1:], 16) z.t0 = int(inp[4][1:], 16) z.ts = int(inp[5][1:], 16) # time base key(s) ntbk = int(inp[6][1:]) Tbk = [] for i in range(ntbk): key = {} key['otp'] = int(inp[7 + (2 * i)][1:], 16) key['T'] = Point.decompress(inp[8 + (2 * i)][1:]) Tbk.append(key) z.Tbk = Tbk z.initialized = True return z def __str__(self): return self.serialize().decode() def __repr__(self): return 'PublicKey.deserialize(' + self.serialize() + ')'
def post(self, pubkey=None): if pubkey is None: self.set_status(400) self.finish() #logging.info('onion: received request') if len(pubkey) != 66: self.set_status(400) self.finish() #logging.info('onion: validated keylength') try: P = Point.decompress(pubkey) except: self.set_status(400) self.finish() ecdh = P * server_p keybin = hashlib.sha256(ecdh.compress()).digest() b = self.request.body bd = base64.b64decode(b) nakpubraw = hexlify(bd[:33]) if not opts['standalone']: nakpub = ncache.get_nak(nakpubraw) if nakpub is None: self.set_status(400) self.finish() sig = (int(hexlify(bd[33:65]), 16), int(hexlify(bd[65:97]), 16)) if not nakpub.verify(sig, bd[97:]): self.set_status(400) self.finish() logging.info('validated access for NAK %s' % nakpubraw) ivcount = int(hexlify(bd[97:113]), 16) counter = Counter.new(128, initial_value=ivcount) cryptor = AES.new(keybin, AES.MODE_CTR, counter=counter) plaintext = cryptor.decrypt(bd[113:]) plaintext = plaintext.decode('UTF-8') #logging.info('onion received: ' + plaintext) o_r = json.loads(plaintext) if o_r['local'] is True: if o_r['action'].lower() == 'get': o_server = 'http://127.0.0.1:' + str(opts['listenport']) + '/' o_path = o_r['url'] self.client_R = Point.decompress(o_r['replykey']) req = tornado.httpclient.HTTPRequest(o_server + o_path, method='GET', headers=o_r['headers'], connect_timeout=30, request_timeout=60) onion_client.fetch(req, self.callback_encrypt) elif o_r['action'].lower() == 'post': o_server = 'http://127.0.0.1:' + str(opts['listenport']) + '/' o_path = o_r['url'] self.client_R = Point.decompress(o_r['replykey']) req = tornado.httpclient.HTTPRequest(o_server + o_path, method='POST', body=o_r['body'], headers=o_r['headers'], connect_timeout=30, request_timeout=60) onion_client.fetch(req, self.callback_encrypt) else: if nak is None: self.set_status(400) self.finish() if (len(o_r['pubkey']) != 66) or not ( (o_r['pubkey'][:2] == '02') or (o_r['pubkey'][:2] == '03')): self.set_status(400) self.finish() try: i = int(o_r['pubkey'], 16) except: self.set_status(400) self.finish() o_server = 'http://' + o_r['host'] + ':' + str(o_r['port']) + '/' o_path = 'onion/' + o_r['pubkey'] o_raw = base64.b64decode(o_r['body']) sig = nak.sign(o_raw) o_body = base64.b64encode(nakpubbin + unhexlify('%064x' % sig[0]) + unhexlify('%064x' % sig[1]) + o_raw) req = tornado.httpclient.HTTPRequest(o_server + o_path, method='POST', body=o_body, connect_timeout=30, request_timeout=60) onion_client.fetch(req, self.callback)
def _decompress(self): self.I = Point.decompress(self._Iraw) self.J = Point.decompress(self._Jraw) self.K = Point.decompress(self._Kraw)
def __init__(self, nbit, b=None, u=None, G=None, friendly=True): # first, find umax, umin such that p < 2**nbit, p > 2**(nbit - 1) # p = 36u**4 + 36u**3 + 24u**2 + 6u + 1 # n = 36u**4 + 36u**3 + 18u**2 + 6u + 1 limit = pow(2, nbit) if u is None: # derive u randomly based on nbit # coarse upper, lower guesses upr = int((limit / 36)**0.25) lwr = 0 mid = (upr + lwr) >> 1 # midpoint algorithm while ((mid != upr) or (mid != lwr)): mid = (upr + lwr) >> 1 pu = _pfunc(upr) pl = _pfunc(lwr) pm = _pfunc(mid) # print("limit goal = %d" % (limit)) # print("lower p(%d) = %d" % (lwr, pl)) # print("mid p(%d) = %d" % (mid, pm)) # print("upper p(%d) = %d" % (upr, pu)) # print("") # pm should be odd, limit even. They should never meet assert (pm != limit) if pm < limit: if lwr == mid: break lwr = mid else: upr = mid umax = mid # repeat for limit = 2 ** (nbit-1) limit = pow(2, (nbit - 1)) upr = int((limit / 36)**0.25) lwr = 0 mid = (upr + lwr) >> 1 while ((mid != upr) or (mid != lwr)): mid = (upr + lwr) >> 1 pu = _pfunc(upr) pl = _pfunc(lwr) pm = _pfunc(mid) #pm should be odd, limit should be even. They should never meet assert (pm != limit) if pm < limit: if lwr == mid: break lwr = mid else: upr = mid umin = mid else: # u is a parameter umax = u umin = umax print("limit goal = %X" % (pow(2, nbit))) print("umax = %X, pmax = %X" % (umax, _pfunc(umax))) print("umin = %X, pmin = %X" % (umin, _pfunc(umin))) print("(%d potential u values, approx 2 ** %d)" % ((umax - umin) + 1, int(math.log((umax - umin) + 1, 2) + 0.5))) # choose u at random until valid solution, follow algorithm 1 from: # https://www.cryptojedi.org/papers/pfcpo.pdf self.u = 0 if u is None: while True: urand = random.randint(umin, umax) #urand = 6518589491078791937 p = _pfunc(urand) #assert p == 65000549695646603732796438742359905742825358107623003571877145026864184071783 if isPrime(p) != True: continue n = _nfunc(urand) #assert n == 65000549695646603732796438742359905742570406053903786389881062969044166799969 if isPrime(n) != True: continue if (p % 4) == 3: if ((p * p) % 16) == 9: self.u = urand self.p = p self.n = n break else: p = _pfunc(umax) n = _nfunc(umax) self.u = umax self.p = p self.n = n if b is not None: friendly = False # print("u = %X (%d)" % (self.u, self.u)) # print("p = %X (%d)" % (self.p, self.p)) # print("n = %X (%d)" % (self.n, self.n)) if friendly: # print("searching for b") while True: #select friendly parameters per Pereira et al #we happen to choose d as pure imaginary such that Xi is not c = random.randint(0, self.p) di = random.randint(0, self.p) b = pow(c, 4, self.p) - pow(di, 6, self.p) if b != (b % self.p): continue if _legendre(b + 1, self.p) != 1: continue y = _mod_sqrt(b + 1, self.p) # print("trying b = %X, y = %X, y**2 = %X" % (b, y, ((y * y) % p))) p2 = self.p * self.p # curve1 = { 'p': self.p, 'a': 0, 'b': b, 'n': self.n, 'bits': nbit } Generator.set_curve(curve1) P = Point(pow(di, 2, self.p), pow(c, 2, self.p)) assert P.is_valid() Pnm1 = (self.n - 1) * P Ptst = Pnm1 + P if Ptst.is_infinite != True: continue self.G = Generator(pow(di, 2, self.p), pow(c, 2, self.p)) self.b = b Xi = pow(c, 2, p2) + pow(di, 3, p2) bprime = (b * _modinv(Xi, p2)) h = (2 * self.p) - self.n curve2 = { 'p': self.p * self.p, 'a': 0, 'b': bprime, 'n': self.n, 'bits': 2 * nbit } break else: if G is None: if b is None: b = 0 else: b = b - 1 while True: b = b + 1 if _legendre(b + 1, self.p) != 1: # print("b = %d but %d is not quadratic residue" % (b, b+1)) continue y = _mod_sqrt(b + 1, self.p) # print("trying b = %X, y = %X, y**2 = %X" % (b, y, ((y * y) % p))) curve = { 'p': self.p, 'a': 0, 'b': b, 'n': self.n, 'bits': nbit } Generator.set_curve(curve) P = Point(1, y) assert P.is_valid() Pnm1 = (self.n - 1) * P Ptst = Pnm1 + P Pa = P.affine() if Ptst.is_infinite == True: self.G = Generator(Pa[0], Pa[1]) self.b = b break else: assert b is not None curve = { 'p': self.p, 'a': 0, 'b': b, 'n': self.n, 'bits': nbit } Generator.set_curve(curve) P = Point(G[0], G[1]) # print("P = %s" % (P.uncompressed_format())) assert P.is_valid() Pnm1 = (self.n - 1) * P Ptst = Pnm1 + P Pa = P.affine() assert Ptst.is_infinite == True self.G = Generator(Pa[0], Pa[1]) self.b = b #self.p = _pfunc(self.u) #self.n = _nfunc(self.u) print("u = %X (%d)" % (self.u, self.u)) print("p = %X (%d)" % (self.p, self.p)) print("n = %X (%d)" % (self.n, self.n)) print("b = %X (%d)" % (self.b, self.b)) print("P (compressed) = %s" % (self.G.compress())) print("P (uncompressed) = %s" % (self.G.uncompressed_format()))