def file_url_expired_keys(blockchain_id): """ Make a URL to the expired key list """ url = blockstack_client.make_mutable_data_url(blockchain_id, "%s-old" % APP_NAME, None) return url
def file_delete(blockchain_id, data_name, config_path=CONFIG_PATH, wallet_keys=None): """ Remove a file Return {'status': True} on success Return {'error': error} on failure """ config_dir = os.path.dirname(config_path) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME) proxy = blockstack_client.get_default_proxy(config_path=client_config_path) fq_data_name = file_fq_data_name(data_name) res = blockstack_client.data_delete( blockstack_client.make_mutable_data_url(blockchain_id, fq_data_name, None), proxy=proxy, wallet_keys=wallet_keys) if 'error' in res: log.error("Failed to delete: %s" % res['error']) return {'error': 'Failed to delete'} return {'status': True}
def file_get(blockchain_id, hostname, sender_blockchain_id, data_name, output_path, passphrase=None, config_path=CONFIG_PATH, wallet_keys=None): """ Get a file from a known sender. Store it to output_path Return {'status': True} on success Return {'error': error} on failure """ config_dir = os.path.dirname(config_path) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME) proxy = blockstack_client.get_default_proxy(config_path=client_config_path) # get the ciphertext fq_data_name = file_fq_data_name(data_name) res = blockstack_client.data_get(blockstack_client.make_mutable_data_url( sender_blockchain_id, fq_data_name, None), wallet_keys=wallet_keys, proxy=proxy) if 'error' in res: log.error("Failed to get ciphertext for %s: %s" % (fq_data_name, res['error'])) return {'error': 'Failed to get encrypted file'} # stash fd, path = tempfile.mkstemp(prefix="blockstack-file-") f = os.fdopen(fd, "w") f.write(res['data']['ciphertext']) f.flush() os.fsync(f.fileno()) f.close() sender_key_id = res['data']['sender_key_id'] # decrypt it res = file_decrypt(blockchain_id, hostname, sender_blockchain_id, sender_key_id, path, output_path, passphrase=passphrase, config_path=config_path, wallet_keys=wallet_keys) os.unlink(path) if 'error' in res: log.error("Failed to decrypt: %s" % res['error']) return {'error': 'Failed to decrypt data'} else: # success! return res
def gpg_list_app_keys(blockchain_id, appname, proxy=None, wallet_keys=None, config_dir=None): """ List the set of available GPG keys tagged for a given application. Return list of {'keyName': key name, 'contentUrl': URL to key data} Raise on error """ assert is_valid_appname(appname) config_dir = get_config_dir(config_dir) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME) if proxy is None: proxy = blockstack_client.get_default_proxy( config_path=client_config_path) key_info = [] key_prefix = "gpg.%s." % appname # immutable data key listing (look for keys that start with 'appname:') immutable_listing = list_immutable_data(blockchain_id, proxy=proxy) if 'error' in immutable_listing: raise Exception("Blockstack error: %s" % immutable_listing['error']) for immutable in immutable_listing['data']: name = immutable['data_id'] data_hash = immutable['hash'] if name.startswith(key_prefix): key_info.append({ 'keyName': name[len(key_prefix):], 'contentUrl': make_immutable_data_url(blockchain_id, name, data_hash) }) # mutable data key listing (look for keys that start with 'appname:') mutable_listing = list_mutable_data(blockchain_id, proxy=proxy, wallet_keys=wallet_keys) if 'error' in mutable_listing: raise Exception("Blockstack error: %s" % mutable_listing['error']) for mutable in mutable_listing['data']: name = mutable['data_id'] version = mutable['version'] if name.startswith(key_prefix): key_info.append({ 'keyName': name[len(key_prefix):], 'contentUrl': make_mutable_data_url(blockchain_id, name, version) }) return key_info
def gpg_app_get_key(blockchain_id, appname, keyname, immutable=False, key_id=None, key_hash=None, key_version=None, proxy=None, config_dir=None): """ Get an app-specific GPG key. Return {'status': True, 'key_id': ..., 'key': ..., 'app_name': ...} on success return {'error': ...} on error """ assert is_valid_appname(appname) assert is_valid_keyname(keyname) if config_dir is None: config_dir = get_config_dir() fq_key_name = "gpg.%s.%s" % (appname, keyname) key_url = None if immutable: # try immutable key_url = blockstack_client.make_immutable_data_url( blockchain_id, fq_key_name, key_hash) else: # try mutable key_url = blockstack_client.make_mutable_data_url( blockchain_id, fq_key_name, key_version) log.debug("fetch '%s'" % key_url) key_data = gpg_fetch_key(key_url, key_id=key_id, config_dir=config_dir) if key_data is None: return {'error': 'Failed to fetch key'} if key_id is None: key_id = gpg_key_fingerprint(key_data, config_dir=config_dir) ret = { 'status': True, 'key_id': key_id, 'key_data': key_data, 'app_name': appname } return ret
def gpg_list_app_keys( blockchain_id, appname, proxy=None, wallet_keys=None, config_dir=None ): """ List the set of available GPG keys tagged for a given application. Return list of {'keyName': key name, 'contentUrl': URL to key data} Raise on error """ raise Exception("BROKEN; depends on list_mutable_data") assert is_valid_appname(appname) config_dir = get_config_dir( config_dir ) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME ) if proxy is None: proxy = blockstack_client.get_default_proxy( config_path=client_config_path ) key_info = [] key_prefix = "gpg.%s." % appname # immutable data key listing (look for keys that start with 'appname:') immutable_listing = list_immutable_data( blockchain_id, proxy=proxy ) if 'error' in immutable_listing: raise Exception("Blockstack error: %s" % immutable_listing['error']) for immutable in immutable_listing['data']: name = immutable['data_id'] data_hash = immutable['hash'] if name.startswith( key_prefix ): key_info.append( { 'keyName': name[len(key_prefix):], 'contentUrl': make_immutable_data_url( blockchain_id, name, data_hash ) }) # mutable data key listing (look for keys that start with 'appname:') # TODO: use 'accounts' mutable_listing = list_mutable_data( blockchain_id, proxy=proxy, wallet_keys=wallet_keys ) if 'error' in mutable_listing: raise Exception("Blockstack error: %s" % mutable_listing['error']) for mutable in mutable_listing['data']: name = mutable['data_id'] version = mutable['version'] if name.startswith( key_prefix ): key_info.append( { 'keyName': name[len(key_prefix):], 'contentUrl': make_mutable_data_url( blockchain_id, name, version ) }) return key_info
def gpg_list_app_keys(blockchain_id, appname, proxy=None, wallet_keys=None): """ List the set of available GPG keys tagged for a given application. The keys will have the format: gpg/appname/keyname Return list of {'identifier': key ID, 'contentUrl': URL to key data} Raise on error """ key_info = [] key_prefix = "gpg/%s/" % appname # immutable data key listing (look for keys that start with 'appname:') immutable_listing = list_immutable_data(blockchain_id, proxy=proxy) if 'error' in immutable_listing: raise Exception("Blockstack error: %s" % key_listing['error']) for immutable in immutable_listing['data']: name = immutable['data_id'] data_hash = immutable['hash'] if name.startswith(key_prefix): key_info.append({ 'identifier': name, 'contentUrl': make_immutable_data_url(blockchain_id, name, data_hash) }) # mutable data key listing (look for keys that start with 'appname:') mutable_listing = list_mutable_data(blockchain_id, proxy=proxy, wallet_keys=wallet_keys) if 'error' in mutable_listing: raise Exception("Blockstack error: %s" % mutable_listing['error']) for mutable in mutable_listing['data']: name = mutable['data_id'] version = mutable['version'] if name.startswith(key_prefix): key_info.append({ 'identifier': name, 'contentUrl': make_mutable_data_url(blockchain_id, name, version) }) return key_info
def file_delete( blockchain_id, data_name, config_path=CONFIG_PATH, wallet_keys=None ): """ Remove a file Return {'status': True} on success Return {'error': error} on failure """ config_dir = os.path.dirname(config_path) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME ) proxy = blockstack_client.get_default_proxy( config_path=client_config_path ) fq_data_name = file_fq_data_name( data_name ) res = blockstack_client.data_delete( blockstack_client.make_mutable_data_url( blockchain_id, fq_data_name, None ), proxy=proxy, wallet_keys=wallet_keys ) if 'error' in res: log.error("Failed to delete: %s" % res['error']) return {'error': 'Failed to delete'} return {'status': True}
def gpg_app_get_key( blockchain_id, appname, keyname, immutable=False, key_id=None, key_hash=None, key_version=None, proxy=None, config_dir=None ): """ Get an app-specific GPG key. Return {'status': True, 'key_id': ..., 'key': ..., 'app_name': ...} on success return {'error': ...} on error """ assert is_valid_appname(appname) assert is_valid_keyname(keyname) if config_dir is None: config_dir = get_config_dir() fq_key_name = "gpg.%s.%s" % (appname, keyname) key_url = None if immutable: # try immutable key_url = blockstack_client.make_immutable_data_url( blockchain_id, fq_key_name, key_hash ) else: # try mutable key_url = blockstack_client.make_mutable_data_url( blockchain_id, fq_key_name, key_version ) log.debug("fetch '%s'" % key_url) key_data = gpg_fetch_key( key_url, key_id, config_dir=config_dir ) if key_data is None: return {'error': 'Failed to fetch key'} if key_id is None: key_id = gpg_key_fingerprint( key_data, config_dir=config_dir ) ret = { 'status': True, 'key_id': key_id, 'key_data': key_data, 'app_name': appname } return ret
def file_get( blockchain_id, hostname, sender_blockchain_id, data_name, output_path, passphrase=None, config_path=CONFIG_PATH, wallet_keys=None ): """ Get a file from a known sender. Store it to output_path Return {'status': True} on success Return {'error': error} on failure """ config_dir = os.path.dirname(config_path) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME ) proxy = blockstack_client.get_default_proxy( config_path=client_config_path ) # get the ciphertext fq_data_name = file_fq_data_name( data_name ) res = blockstack_client.data_get( blockstack_client.make_mutable_data_url( sender_blockchain_id, fq_data_name, None ), wallet_keys=wallet_keys, proxy=proxy ) if 'error' in res: log.error("Failed to get ciphertext for %s: %s" % (fq_data_name, res['error'])) return {'error': 'Failed to get encrypted file'} # stash fd, path = tempfile.mkstemp( prefix="blockstack-file-" ) f = os.fdopen(fd, "w") f.write( res['data']['ciphertext'] ) f.flush() os.fsync(f.fileno()) f.close() sender_key_id = res['data']['sender_key_id'] # decrypt it res = file_decrypt( blockchain_id, hostname, sender_blockchain_id, sender_key_id, path, output_path, passphrase=passphrase, config_path=config_path, wallet_keys=wallet_keys ) os.unlink( path ) if 'error' in res: log.error("Failed to decrypt: %s" % res['error']) return {'error': 'Failed to decrypt data'} else: # success! return res
def app_download( sender_blockchain_id, appname, config_path=CONFIG_PATH, wallet_keys=None, version=None, appdir=None ): """ Fetch the application to a temporary location on disk. Verify that it was signed by its creator. Decompress the contents. Return {'status': True, 'root': path, 'tag': version or hash} on success Return {'error': ...} on error """ config_dir = os.path.dirname(config_path) cache_dir = os.path.join( config_dir, CACHE_DIRNAME ) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME ) file_config_path = os.path.join(config_dir, blockstack_file.CONFIG_PATH ) proxy = blockstack_client.get_default_proxy( config_path=client_config_path ) if appdir is None: appdir = tempfile.mkdtemp(dir=cache_dir) # get the app data fq_data_name = app_fq_data_name( appname ) res = blockstack_client.data_get( blockstack_client.make_mutable_data_url( sender_blockchain_id, fq_data_name, version ), wallet_keys=wallet_keys, proxy=proxy ) if 'error' in res: log.error("Failed to get data for %s: %s" % (fq_data_name, res['error'])) return {'error': 'Failed to get app data'} tag = res.get('version', None) if tag is None: tag = res.get('hash', None) if tag is None: log.error("Missing version or hash") return {'error': "Missing version or hash"} # stash fd, path = tempfile.mkstemp( prefix="blockstack-app-download-", dir=cache_dir ) log.debug("Stash app data to %s" % path) f = os.fdopen(fd, "w") f.write( base64.b64decode(res['data']['app']) ) f.flush() os.fsync(f.fileno()) f.close() sender_key_id = res['data']['sender_key_id'] sig = res['data']['sig'] # verify it res = blockstack_file.file_verify( sender_blockchain_id, sender_key_id, path, sig, config_path=client_config_path, wallet_keys=wallet_keys ) if 'error' in res: log.error("Failed to verify: %s" % res['error']) return {'error': 'Failed to verify app data'} # decompress it try: ziph = zipfile.ZipFile( path, 'r', zipfile.ZIP_DEFLATED ) ziph.extractall( path=appdir ) ziph.close() log.debug("Extracted app to '%s'" % appdir) except zipfile.BadZipfile: log.error("Bad zipfile") return {'error': 'Bad zipfile'} except Exception, e: log.exception(e) return {'error': 'Failed to extract'}
return {'error': 'Failed to sign'} # replicate app_bin = None with open(path, "r") as f: app_bin = f.read() data = { 'sender_key_id': res['sender_key_id'], 'sig': res['sig'], 'app': base64.b64encode( app_bin ) } fq_data_name = app_fq_data_name( app_name ) proxy = blockstack_client.get_default_proxy( config_path=client_config_path ) res = blockstack_client.data_put( blockstack_client.make_mutable_data_url( sender_blockchain_id, fq_data_name, version ), data, wallet_keys=wallet_keys, proxy=proxy ) if 'error' in res: log.error("Failed to upload app '%s': %s" % (fq_data_name, res['error'])) return {'error': 'Failed to upload data'} return {'status': True, 'sender_key_id': data['sender_key_id'], 'sig': data['sig']} def app_download( sender_blockchain_id, appname, config_path=CONFIG_PATH, wallet_keys=None, version=None, appdir=None ): """ Fetch the application to a temporary location on disk. Verify that it was signed by its creator. Decompress the contents. Return {'status': True, 'root': path, 'tag': version or hash} on success Return {'error': ...} on error
def file_put(blockchain_id, hostname, recipient_blockchain_ids, data_name, input_path, passphrase=None, config_path=CONFIG_PATH, wallet_keys=None): """ Send a file to the given recipient, encrypted and signed with the given blockchain ID. Allow each recipient to receive the data on each of their hosts. Return {'status': True} on success, and upload to cloud storage Return {'error': ...} on error """ fd, output_path = tempfile.mkstemp(prefix="blockstack-file-") os.fchmod(fd, 0600) os.close(fd) config_dir = os.path.dirname(config_path) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME) all_recipients = [] # make available to all other hosts for this blockchain_id my_hosts = file_list_hosts(blockchain_id, wallet_keys=wallet_keys, config_path=config_path) if 'error' in my_hosts: log.error("Failed to list hosts: %s" % my_hosts['error']) os.unlink(output_path) return {'error': 'Failed to look up sender keys'} if hostname in my_hosts: my_hosts.remove(hostname) all_recipients += [(blockchain_id, host) for host in my_hosts['hosts']] # make available to all hosts for each recipient for recipient_blockchain_id in recipient_blockchain_ids: their_hosts = file_list_hosts(recipient_blockchain_id, wallet_keys=wallet_keys, config_path=config_path) if 'error' in their_hosts: log.error("Failed to list hosts for %s: %s" % (recipient_blockchain_id, their_hosts['error'])) os.unlink(output_path) return {'error': 'Failed to look up recipient keys'} all_recipients += [(recipient_blockchain_id, host) for host in their_hosts['hosts']] # encrypt res = file_encrypt(blockchain_id, hostname, all_recipients, input_path, output_path, passphrase=passphrase, config_path=config_path, wallet_keys=wallet_keys) if 'error' in res: log.error("Failed to encrypt: %s" % res['error']) os.unlink(output_path) return {'error': 'Failed to encrypt'} # load up with open(output_path, "r") as f: ciphertext = f.read() message = {'ciphertext': ciphertext, 'sender_key_id': res['sender_key_id']} # put to mutable storage fq_data_name = file_fq_data_name(data_name) proxy = blockstack_client.get_default_proxy(config_path=client_config_path) res = blockstack_client.data_put(blockstack_client.make_mutable_data_url( blockchain_id, fq_data_name, None), message, wallet_keys=wallet_keys, proxy=proxy) if 'error' in res: log.error("Failed to put data: %s" % res['error']) os.unlink(output_path) return {'error': 'Failed to replicate data'} os.unlink(output_path) return {'status': True}
def file_put( blockchain_id, hostname, recipient_blockchain_ids, data_name, input_path, passphrase=None, config_path=CONFIG_PATH, wallet_keys=None ): """ Send a file to the given recipient, encrypted and signed with the given blockchain ID. Allow each recipient to receive the data on each of their hosts. Return {'status': True} on success, and upload to cloud storage Return {'error': ...} on error """ fd, output_path = tempfile.mkstemp( prefix="blockstack-file-" ) os.fchmod( fd, 0600 ) os.close(fd) config_dir = os.path.dirname(config_path) client_config_path = os.path.join(config_dir, blockstack_client.CONFIG_FILENAME ) all_recipients = [] # make available to all other hosts for this blockchain_id my_hosts = file_list_hosts( blockchain_id, wallet_keys=wallet_keys, config_path=config_path ) if 'error' in my_hosts: log.error("Failed to list hosts: %s" % my_hosts['error']) os.unlink(output_path) return {'error': 'Failed to look up sender keys'} if hostname in my_hosts: my_hosts.remove(hostname) all_recipients += [(blockchain_id, host) for host in my_hosts['hosts']] # make available to all hosts for each recipient for recipient_blockchain_id in recipient_blockchain_ids: their_hosts = file_list_hosts( recipient_blockchain_id, wallet_keys=wallet_keys, config_path=config_path ) if 'error' in their_hosts: log.error("Failed to list hosts for %s: %s" % (recipient_blockchain_id, their_hosts['error'])) os.unlink(output_path) return {'error': 'Failed to look up recipient keys'} all_recipients += [(recipient_blockchain_id, host) for host in their_hosts['hosts']] # encrypt res = file_encrypt( blockchain_id, hostname, all_recipients, input_path, output_path, passphrase=passphrase, config_path=config_path, wallet_keys=wallet_keys ) if 'error' in res: log.error("Failed to encrypt: %s" % res['error']) os.unlink(output_path) return {'error': 'Failed to encrypt'} # load up with open(output_path, "r") as f: ciphertext = f.read() message = {'ciphertext': ciphertext, 'sender_key_id': res['sender_key_id']} # put to mutable storage fq_data_name = file_fq_data_name( data_name ) proxy = blockstack_client.get_default_proxy( config_path=client_config_path ) res = blockstack_client.data_put( blockstack_client.make_mutable_data_url( blockchain_id, fq_data_name, None ), message, wallet_keys=wallet_keys, proxy=proxy ) if 'error' in res: log.error("Failed to put data: %s" % res['error']) os.unlink(output_path) return {'error': 'Failed to replicate data'} os.unlink(output_path) return {'status': True}
def file_url_expired_keys( blockchain_id ): """ Make a URL to the expired key list """ url = blockstack_client.make_mutable_data_url( blockchain_id, "%s-old" % APP_NAME, None ) return url