def save(self, default=False): if not self.fileroot: self.fileroot = keydir mkdirs(keydir) if not self.filename: self.filename = os.path.join(self.fileroot, self.name) if self.new: fd = os.open(self.filename, os.O_WRONLY | os.O_CREAT | os.O_EXCL) file = os.fdopen(fd, 'w') if not file: raise RuntimeError( 'Key file [{}] already exists'.format(filename)) file.write(self.dump()) file.close() self.new = False else: temp = tempfile.NamedTemporaryFile(dir=self.fileroot, delete=False) temp.write(self.dump()) temp.close() os.rename(temp.name, self.filename) if default: self._write_default(self.filename)
def twisted_main(args): log_observer = log.FileLogObserver(sys.stdout, log.INFO) log_observer.start() # Load state root = appdirs.user_cache_dir(args.instancename, 'zarbosoft') mkdirs(root) state = {} try: with open(os.path.join(root, 'state.json'), 'r') as prestate: state = json.loads(prestate.read()) except Exception as e: if args.verbose: log_info('Failed to load state: {}'.format(e)) republish = state.get('republish', {}) # Set up kademlia kserver = Server( ksize=state.get('ksize', 20), alpha=state.get('alpha', 3), seed=binascii.unhexlify(state['seed']) if 'seed' in state else None, storage=Storage(args)) bootstraps = map(tuple, state.get('bootstrap', [])) for bootstrap in args.bootstrap: bhost, bport = bootstrap.split(':', 2) bport = int(bport) bhost_ip = yield reactor.resolve(bhost) bootstraps.append((bhost_ip, bport)) if args.verbose: log_info('Bootstrapping hosts: {}'.format(bootstraps)) kserver.bootstrap(bootstraps) udpserver = internet.UDPServer(args.dhtport, kserver.protocol) udpserver.startService() # Set up state saver def save_state(): if args.verbose: log_info('Saving state') state['ksize'] = kserver.ksize state['alpha'] = kserver.alpha state['seed'] = binascii.hexlify(kserver.node.seed) state['republish'] = republish state['bootstrap'] = kserver.bootstrappableNeighbors() with open(os.path.join(root, 'state.json.1'), 'w') as prestate: prestate.write(json.dumps(state)) os.rename( os.path.join(root, 'state.json.1'), os.path.join(root, 'state.json')) save_state_loop = LoopingCall(save_state) save_state_loop.start(60) # Set up value republisher def start_republish(): @defer.inlineCallbacks def republish_call(): for key, val in republish.items(): if args.verbose: log_info('Republishing {}'.format(key)) yield kserver.set(key, val) republish_loop = LoopingCall(republish_call) republish_loop.start(1 * 60 * 60 * 24) deferLater(reactor, 60, start_republish) # Set up webserver with open(res('redirect_template.html'), 'r') as template: redirect_template = template.read() class Resource(resource.Resource): def getChild(self, child, request): return self def render_GET(self, request): key = urllib.unquote(request.path[1:]) if key == 'setup': with open(res('browser_setup.html'), 'r') as static: return static.read() if key == 'icon-bizast-off.png': with open(res('icon-bizast-off.png'), 'r') as static: return static.read() if key.startswith(webprotocol): key = key[len(webprotocol):] if key.startswith(webprotocol2): key = key[len(webprotocol2):] if key.count(':') != 1: raise ValueError('Invalid resource id') try: key, path = key.split('/', 1) path = '/' + path except ValueError: path = '' def respond(value): if not value: request.write(NoResource().render(request)) else: valid, ign, ign = validate(args, None, value, None) if not valid: request.write(NoResource('Received invalid resource: {}'.format(value)).render(request)) else: value = json.loads(value) if any('text/html' in val for val in request.requestHeaders.getRawHeaders('Accept', [])): message = value['message'] + path if urlmatch.match(message) and '\'' not in message and '"' not in message: request.write(redirect_template.format( resource=message).encode('utf-8')) else: request.write(message.encode('utf-8')) else: request.write(json.dumps(value)) request.finish() log.msg('GET: key [{}]'.format(key)) d = kserver.get(key) d.addCallback(respond) return server.NOT_DONE_YET def render_POST(self, request): value = request.content.getvalue() valid, rec_key, fingerprint = validate(args, None, value, None) if not valid: raise ValueError('Failed verification') log.msg('SET: key [{}] = val [{}]'.format(rec_key, value)) republish[rec_key] = value def respond(result): request.write('Success') request.finish() d = kserver.set(rec_key, value) d.addCallback(respond) return server.NOT_DONE_YET def render_DELETE(self, request): key = urllib.unquote(request.path[1:]) if key.startswith(webprotocol): key = key[len(webprotocol):] if key.count(':') != 1: raise ValueError('Invalid resource id') if key not in republish: raise ValueError('Not republishing key {}'.format(key)) del republish[key] return 'Success' webserver = internet.TCPServer(args.webport, server.Site(Resource())) webserver.startService()
def main(): parser = argparse.ArgumentParser( description='NaCl key management tool', ) commands = parser.add_subparsers(dest='command') def add_common_subparser(*pargs, **kwargs): out = commands.add_parser(*pargs, **kwargs) out.add_argument( '-v', '--verbose', action='store_true', help='Enable verbose output', ) return out gen_command = add_common_subparser('gen', description='Generate key') gen_command.add_argument( 'name', help='Name of key. Preferably something easy to type', ) gen_command.add_argument( '-n', '--nopassphrase', help='Don\'t encrypt key', action='store_true', ) gen_command.add_argument( '-u', '--dump', help='Print rather than store key', action='store_true', ) gen_command.add_argument( '-a', '--alternate', help='Don\'t make the new key the default', action='store_true', ) mod_command = add_common_subparser('mod', description='Modify key') mod_command.add_argument( '-k', '--key', help='Key name or filename. Use default key if unspecified', ) mod_command.add_argument( '-p', '--passphrase', help='Set or change passphrase', action='store_true', ) mod_command.add_argument( '-r', '--remove-passphrase', help='Remove passphrase', action='store_true', ) mod_command.add_argument( '-d', '--default', help='Make key default', action='store_true', ) args = parser.parse_args() mkdirs(keydir) if args.command == 'gen': key = Key.new(args.name) if args.alternate and args.dump: parser.error('Can\'t specify both alternate and print') if not args.nopassphrase: key.set_passphrase() if args.dump: print(key.dump()) else: key.save(not args.alternate) print(key.fingerprint) elif args.command == 'mod': key = Key.open(args.key, args.passphrase) if args.passphrase or args.remove_passphrase: if args.passphrase: key.set_passphrase() elif args.remove_passphrase: key.remove_passphrase() key.save(args.default)