def init(device_id, password, owner_id, action_names): table = get_tinydb_table(path, 'device') table.upsert( { 'id': device_id, 'password': password, "owner_id": owner_id, "actions": action_names }, Query().id.exists()) db = TinyDB(path) db.purge_table('users') table = get_tinydb_table(path, 'users') table.insert({'integrity': init_integrity_data(), "id": int(owner_id)})
def create_device_type(description, token): if len(get_tinydb_table(path, 'device_type_keys')) == 0: init_device_type_keys() table = get_tinydb_table(path, 'device_type_keys') doc = table.all()[0] desc_ciphertext = encrypt_using_fernet_hex(doc["description"], description) data = { "description": desc_ciphertext, "correctness_hash": correctness_hash(description) } r = requests.post(URL_CREATE_DEVICE_TYPE, headers={"Authorization": token}, data=data, verify=VERIFY_CERTS) click.echo(r.content.decode('unicode-escape'))
def save_column_keys(data): """ Can be trigger by: `./cli.py -b "172.26.0.8" user send-column-keys 1 23` """ try: if bool(re.search('\"\w+:\w+\"', data)): data = json.loads(data) if get_owner_id() != int(data["user_id"]): click.echo("This command is only available for device owner.") return table = get_tinydb_table(path, 'users') doc = table.all()[0] data.pop("user_id", None) fernet_key = hex_to_fernet(doc["shared_key"]) keys = {} for k, v in data.items(): if k == "device_data:data": keys[k] = json.loads( fernet_key.decrypt(data[k].encode()).decode()) else: keys[k] = key_to_hex(fernet_key.decrypt(data[k].encode())) doc = {**doc, **keys} table.update(doc) except Exception as e: # pragma: no exc cover _, _, exc_tb = sys.exc_info() line = exc_tb.tb_lineno click.echo(f"{repr(e)} at line: {line}")
def send_key_to_device(device_id, token): if not is_device_bi_key_missing( device_id, create_device, "Blind index key for device name is missing"): private_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) public_key = private_key.public_key() public_pem = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8') private_pem = private_key.private_bytes( encoding=serialization.Encoding.PEM, format=serialization.PrivateFormat.TraditionalOpenSSL, encryption_algorithm=serialization.NoEncryption()).decode() table = get_tinydb_table(path, 'device_keys') table.upsert( { 'device_id': device_id, 'public_key': public_pem, 'private_key': private_pem, 'bi_key': key_to_hex(get_device_bi_key(device_id)) }, Query().device_id == device_id) data = {'device_id': device_id, 'public_key': public_pem} r = requests.post(URL_START_KEY_EXCHANGE, headers={"Authorization": token}, data=data, verify=VERIFY_CERTS) click.echo(r.content.decode('unicode-escape'))
def attr_auth_device_keygen(device_id, attr_list, token): if device_id not in " ".join(attr_list): click.echo( f"attr_list argument should contain device_id ({device_id})") return doc = search_tinydb_doc(path, 'aa_keys', where('public_key').exists()) if not doc: with click.Context(get_attr_auth_keys) as ctx: click.echo( f"Public key not present, please use: {ctx.command.name}") click.echo(get_attr_auth_keys.get_help(ctx)) return data = {"attr_list": " ".join(attr_list)} r = requests.post(AA_URL_DEVICE_KEYGEN, headers={"Authorization": token}, data=data, verify=VERIFY_CERTS) content = r.content.decode('unicode-escape') json_content = json_string_with_bytes_to_dict(content) if not json_content["success"]: click.echo(json_content) return t = get_tinydb_table(path, "device_keys") device_data_doc = { "private_key": json_content["private_key"], "attr_list": attr_list, } t.update(set("device_data:data", device_data_doc), Query().device_id == device_id)
def init_scene_keys(): table = get_tinydb_table(path, 'scene_keys') table.upsert( { 'name': key_to_hex(os.urandom(32)), 'description': key_to_hex(os.urandom(32)) }, where('name').exists() & where('description').exists())
def init_global_keys(): table = get_tinydb_table(path, 'global') table.upsert( { 'bi_key': key_to_hex(os.urandom(32)), 'scene_key': key_to_hex(os.urandom(32)), }, where('bi_key').exists() & where('scene_key').exists())
def create_scene(name, description, token): if not is_global_bi_key_missing( init_global_keys, "Blind index key for scene name is missing"): if len(get_tinydb_table(path, 'scene_keys')) == 0: init_scene_keys() table = get_tinydb_table(path, 'scene_keys') doc = table.all()[0] name_ciphertext = encrypt_using_fernet_hex(doc["name"], name) desc_ciphertext = encrypt_using_fernet_hex(doc["description"], description) data = { "name": name_ciphertext, "correctness_hash": correctness_hash(name), "name_bi": blind_index(get_global_bi_key(), name), "description": desc_ciphertext } r = requests.post(URL_CREATE_SCENE, headers={"Authorization": token}, data=data, verify=VERIFY_CERTS) click.echo(r.content.decode('unicode-escape'))
def send_column_keys(user_id, device_id, policy): table = get_tinydb_table(path, 'device_keys') doc = table.get(Query().device_id == device_id) if not doc: with click.Context(send_key_to_device) as ctx: click.echo( f"Keys for device {device_id} not present, please use: {ctx.command.name}" ) click.echo(get_attr_auth_keys.get_help(ctx)) return fernet_key = hex_to_fernet(doc["shared_key"]) keys = { "action:name": None, "device_data:added": None, "device_data:num_data": None, "device_data:tid": None } payload_keys = {} for k in keys: random_bytes = os.urandom(32) keys[k] = key_to_hex( random_bytes) # NOTE: retrieve key as `key_to_hex(key)` payload_keys[k] = fernet_key.encrypt(random_bytes).decode() # payload_keys["device_data:data"] = fernet_key.encrypt(get_aa_public_key().encode()).decode() abe_key_and_policy = json.dumps({ "public_key": get_aa_public_key(), "policy": policy }).encode() payload_keys["device_data:data"] = fernet_key.encrypt( abe_key_and_policy).decode() payload_keys["device:name"] = fernet_key.encrypt( hex_to_key(doc["device:name"])).decode() payload_keys["device:status"] = fernet_key.encrypt( hex_to_key(doc["device:status"])).decode() payload_keys["bi_key"] = fernet_key.encrypt(hex_to_key( doc["bi_key"])).decode() payload_keys["scene_key"] = fernet_key.encrypt( get_global_scene_key()).decode() doc = {**doc, **keys} table.upsert(doc, Query().device_id == device_id) client = _setup_client(user_id) payload = f'"{json.dumps(_create_payload(payload_keys, user_id))}"' ret = client.publish(f"u:{user_id}/d:{device_id}/", payload) click.echo(f"RC and MID = {ret}")
def get_attr_auth_keys(token): r = requests.get(AA_URL_SETUP, headers={"Authorization": token}, verify=VERIFY_CERTS) content = json.loads(r.content.decode('unicode-escape')) click.echo(f"Saving keys to {path}") table = get_tinydb_table(path, 'aa_keys') doc = table.get(where('public_key').exists()) data = {"public_key": content["public_key"]} if doc: table.update(data) else: table.insert(data)
def retrieve_device_public_key(device_id, token): data = {"device_id": device_id} table = get_tinydb_table(path, 'device_keys') doc = table.get(Query().device_id == device_id) if not doc: with click.Context(send_key_to_device) as ctx: click.echo( f"Keys for device {device_id} not present, please use: {ctx.command.name}" ) click.echo(get_attr_auth_keys.get_help(ctx)) return r = requests.post(URL_RECEIVE_PUBLIC_KEY, headers={"Authorization": token}, data=data, verify=VERIFY_CERTS) if r.status_code != 200: click.echo(r.content.decode('unicode-escape')) return content = r.content.decode('unicode-escape') json_content = json_string_with_bytes_to_dict(content) private_key = load_pem_private_key(doc["private_key"].encode(), password=None, backend=default_backend()) assert isinstance( private_key, EllipticCurvePrivateKey ), "Loading private key failed! - private_key is not instance of EllipticCurvePrivateKey" device_public_key = load_pem_public_key( json_content["device_public_key"].encode(), backend=default_backend()) assert isinstance( device_public_key, EllipticCurvePublicKey ), "Loading public key failed! - private_key is not instance of EllipticCurvePublicKey" shared_key = private_key.exchange(ec.ECDH(), device_public_key) derived_key = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=b'handshake data', backend=default_backend()).derive(shared_key) key = key_to_hex(derived_key) # NOTE: retrieve key as `key_to_hex(key)` table.update(delete("public_key"), Query().device_id == device_id) table.update(delete("private_key"), Query().device_id == device_id) table.update(set("shared_key", key), Query().device_id == device_id)
def register_to_broker(password, token): password_hash = pbkdf2_hash(password) data = {"password": password_hash} r = requests.post(URL_REGISTER_TO_BROKER, headers={"Authorization": token}, data=data, verify=VERIFY_CERTS) content = json.loads(r.content.decode('unicode-escape')) table = get_tinydb_table(path, 'credentials') table.upsert( { "broker_id": content["broker_id"], "broker_password": password }, Query().broker_id == content["broker_id"]) click.echo(r.content.decode('unicode-escape'))
def receive_pk(data): try: if "user_public_key" in data: json_data = json.loads(data.replace("'", '"'), strict=False) pk_user_pem = json.dumps(json_data['user_public_key']) user_id = int(json.dumps(json_data['user_id'])[1:-1]) if get_owner_id() != user_id: click.echo("This command is only available for device owner.") return key = pk_user_pem[1:-1].encode('utf-8').replace(b"\\n", b"\n") public_key = load_pem_public_key(key, backend=default_backend()) assert isinstance(public_key, EllipticCurvePublicKey) private_key = ec.generate_private_key(ec.SECP384R1(), default_backend()) shared_key = private_key.exchange(ec.ECDH(), public_key) derived_key = HKDF(algorithm=hashes.SHA256(), length=32, salt=None, info=b'handshake data', backend=default_backend()).derive(shared_key) table = get_tinydb_table(path, 'users') key = key_to_hex( derived_key) # NOTE: retrieve key as `key_to_hex(key)` table.upsert({ 'id': user_id, 'shared_key': key, 'tid': -1 }, Query().id == user_id) public_key = private_key.public_key() public_pem = public_key.public_bytes( encoding=serialization.Encoding.PEM, format=serialization.PublicFormat.SubjectPublicKeyInfo).decode( 'utf-8').replace("\n", "\\n") payload = f'{{"user_id": {int(user_id)}, "device_public_key": "{public_pem}"}}' click.echo(payload) except Exception as e: # pragma: no exc cover _, _, exc_tb = sys.exc_info() line = exc_tb.tb_lineno click.echo(f"{repr(e)} at line: {line}")
def get_devices(device_name, device_id, token): """Triggered using: ./cli.py -b "172.26.0.8" user get-devices test_device 46 --token 5c36ab84439c55a3c196f4csd9bd7b3d9291f39g""" device_name_bi = blind_index(get_device_bi_key(device_id), device_name) data = {"name_bi": device_name_bi} r = requests.get(URL_GET_DEVICE, headers={"Authorization": token}, params=data, verify=VERIFY_CERTS) content = json.loads(r.content.decode('unicode-escape')) table = get_tinydb_table(path, 'device_keys') for device in content["devices"]: ciphertext = device["name"] doc = table.get(Query().device_id == str(device["id"])) plaintext = decrypt_using_fernet_hex(doc["device:name"], ciphertext) device["name"] = plaintext.decode() check_correctness_hash(content["devices"], "name") click.echo(content["devices"])
def get_fake_tuple(bound): try: table = get_tinydb_table(path, 'users') doc = get_user_data() if bound == "lower_bound" and not can_remove_fake_row( doc["integrity"]["device_data"]): return # "Can't remove row that was not yet inserted (lower bound equals upper bound)" fake_tuple = {**generate(doc["integrity"]["device_data"], bound=bound)} fake_tuple["data"] = pad_payload_attr(str(fake_tuple["data"]), fake=True) keys = get_key_type_pair(doc) fake_tuple_hash = correctness_hash([ fake_tuple["added"], fake_tuple["data"], fake_tuple["num_data"], fake_tuple["tid"] ]) encrypted_fake_tuple = encrypt_row(fake_tuple, keys) row = { **encrypted_fake_tuple, "correctness_hash": fake_tuple_hash, "tid_bi": blind_index(hex_to_key(get_bi_key()), str(fake_tuple["tid"])) } if bound == "upper_bound": doc["integrity"]["device_data"] = increment_bounds( doc["integrity"]["device_data"]) if bound == "lower_bound": doc["integrity"]["device_data"] = increment_bounds( doc["integrity"]["device_data"], bound=bound) payload = dict_to_payload(**row) table.update(doc) click.echo(payload) except Exception as e: # pragma: no exc cover _, _, exc_tb = sys.exc_info() line = exc_tb.tb_lineno click.echo(f"{repr(e)} at line: {line}")
def get_global_scene_key(): table = get_tinydb_table(path, 'global') doc_global = table.all()[0] return hex_to_key(doc_global["scene_key"])
def get_self_id(): table = get_tinydb_table(path, 'device') device_id = table.all()[0]["id"] return device_id
def get_next_tid(): table = get_tinydb_table(path, 'users') tid = table.all()[0]["tid"] table.update(decrement('tid')) return tid
def get_user_data(): table = get_tinydb_table(path, 'users') return table.all()[0]
def get_bi_key(): table = get_tinydb_table(path, 'users') return table.all()[0]["bi_key"]
def init_device_type_keys(): table = get_tinydb_table(path, 'device_type_keys') table.upsert({ 'description': key_to_hex(os.urandom(32)), }, where('description').exists())
def get_owner_id(): table = get_tinydb_table(path, 'device') return int(table.all()[0]["owner_id"])