async def sign_transaction_or_proof_start(ctx, msg_tx: BytecoinSignStartRequest, msg_proof: BytecoinStartProofRequest, keychain): creds = misc.get_creds(keychain) state = SignState(creds) if msg_tx is not None: if msg_tx.inputs_size == 0: raise wire.DataError("Invalid number of inputs") if msg_tx.outputs_size == 0: raise wire.DataError("Invalid number of outputs") state.state = State.EXPECT_ADD_INPUT state.inputs_size = msg_tx.inputs_size state.outputs_size = msg_tx.outputs_size state.extra_size = msg_tx.extra_size state.tx_prefix_stream.update(bcncrypto.get_varint_data(msg_tx.version)) state.tx_prefix_stream.update(bcncrypto.get_varint_data(msg_tx.ut)) state.tx_prefix_stream.update(bcncrypto.get_varint_data(msg_tx.inputs_size)) state.tx_inputs_stream.update(bcncrypto.get_varint_data(msg_tx.inputs_size)) else: state.state = State.EXPECT_ADD_EXTRA_CHUNK state.inputs_size = 1 state.extra_size = msg_proof.data_size state.tx_prefix_stream.update(b'\x00') res = BytecoinEmptyResponse() while True: msg = await ctx.call( res, MessageType.BytecoinSignAddInputRequest, MessageType.BytecoinSignAddOutputRequest, MessageType.BytecoinSignAddExtraRequest, MessageType.BytecoinSignStepARequest, MessageType.BytecoinSignStepAMoreDataRequest, MessageType.BytecoinSignGetC0Request, MessageType.BytecoinSignStepBRequest, ) del res if msg.MESSAGE_WIRE_TYPE == MessageType.BytecoinSignAddInputRequest: res = await _sign_add_input(state, ctx, msg) elif msg.MESSAGE_WIRE_TYPE == MessageType.BytecoinSignAddOutputRequest: res = await _sign_add_output(state, ctx, msg) elif msg.MESSAGE_WIRE_TYPE == MessageType.BytecoinSignAddExtraRequest: res = await _sign_add_extra_chunkt(state, ctx, msg) elif msg.MESSAGE_WIRE_TYPE == MessageType.BytecoinSignStepARequest: res = await _sign_step_a(state, ctx, msg) elif msg.MESSAGE_WIRE_TYPE == MessageType.BytecoinSignStepAMoreDataRequest: res = await _sign_step_a_more_data(state, ctx, msg) elif msg.MESSAGE_WIRE_TYPE == MessageType.BytecoinSignGetC0Request: res = await _sign_get_c0(state, ctx, msg) elif msg.MESSAGE_WIRE_TYPE == MessageType.BytecoinSignStepBRequest: res = await _sign_step_b(state, ctx, msg) else: raise wire.DataError("Invalid message") if state.state == State.FINISHED: break gc.collect() return res
async def _sign_step_b(state, ctx, msg: BytecoinSignStepBRequest): if msg.address_index is None: msg.address_index = 0 if state.state != State.EXPECT_STEP_B or state.inputs_counter >= state.inputs_size: raise wire.DataError("Unexpected sign step b") state.tx_prefix_stream.update(msg.output_secret_hash_arg) state.tx_prefix_stream.update(bcncrypto.get_varint_data(msg.address_index)) address_audit_secret_key = state.creds.generate_address_secret_key(msg.address_index) inv_output_secret_hash = misc.invert_arg_to_inv_hash(msg.output_secret_hash_arg) output_secret_key_a = inv_output_secret_hash.mul(address_audit_secret_key) output_secret_key_s = inv_output_secret_hash.mul(state.creds.spend_secret_key) kr = state.generate_sign_secret(state.inputs_counter, b"kr") ks = state.generate_sign_secret(state.inputs_counter, b"ks") ka = state.generate_sign_secret(state.inputs_counter, b"ka") sig_rs = ks.sub(state.c0.mul(output_secret_key_s)) sig_ra = ka.add(state.c0.mul(output_secret_key_a)) my_c = bcncrypto.BcnScalar(msg.my_c) sig_rr = kr.sub(my_c.mul(output_secret_key_a)) esig_rs = state.encrypt_result(sig_rs.to_bytes(), state.inputs_counter, b"rs") esig_ra = state.encrypt_result(sig_ra.to_bytes(), state.inputs_counter, b"ra") esig_rr = state.encrypt_result(sig_rr.to_bytes(), state.inputs_counter, b"rr") state.inputs_counter += 1 e_key = bytes(32) if state.inputs_counter >= state.inputs_size: state.state = State.FINISHED step_args_hash2 = state.tx_prefix_stream.digest() if step_args_hash2 == state.step_args_hash: e_key = state.encryption_key return BytecoinSignStepBResponse(my_rr=esig_rr, ra=esig_ra, rs=esig_rs, encryption_key=e_key)
async def _sign_step_a(state, ctx, msg: BytecoinSignStepARequest): if msg.address_index is None: msg.address_index = 0 if state.state == State.EXPECT_STEP_A_MORE_DATA and state.inputs_counter + 1 < state.inputs_size: state.inputs_counter += 1 state.state = State.EXPECT_STEP_A if state.state != State.EXPECT_STEP_A or state.inputs_counter >= state.inputs_size: raise wire.DataError("Unexpected sign step a") state.tx_prefix_stream.update(msg.output_secret_hash_arg) state.tx_prefix_stream.update(bcncrypto.get_varint_data(msg.address_index)) address_audit_secret_key = state.creds.generate_address_secret_key(msg.address_index) inv_output_secret_hash = misc.invert_arg_to_inv_hash(msg.output_secret_hash_arg) output_secret_key_a = inv_output_secret_hash.mul(address_audit_secret_key) output_secret_key_s = inv_output_secret_hash.mul(state.creds.spend_secret_key) output_public_key = misc.secret_keys_to_public_key(output_secret_key_a, output_secret_key_s) keyimage_b = misc.generate_key_image_b(output_public_key, output_secret_key_a) b_coin = bcncrypto.hash_to_ec(keyimage_b) hash_my_pub = bcncrypto.hash_to_ec(output_public_key.to_bytes()) sig_p = output_secret_key_s.scalarmult_h().sub(output_secret_key_a.scalarmult(b_coin)) state.tx_inputs_stream.update(sig_p.to_bytes()) kr = state.generate_sign_secret(state.inputs_counter, b"kr") ks = state.generate_sign_secret(state.inputs_counter, b"ks") ka = state.generate_sign_secret(state.inputs_counter, b"ka") x = ks.scalarmult_h().add(ka.scalarmult(b_coin)) state.tx_inputs_stream.update(x.to_bytes()) y = kr.scalarmult_base().add(kr.scalarmult(b_coin)) z = kr.scalarmult(hash_my_pub) state.state = State.EXPECT_STEP_A_MORE_DATA return BytecoinSignStepAResponse(sig_p=sig_p.to_bytes(),y=y.to_bytes(),z=z.to_bytes())
def generate_output_seed(self, tx_inputs_hash: bytes, out_index: int) -> bytes: add = bcncrypto.get_varint_data(out_index) kck = bcncrypto.get_keccak() kck.update(self.view_seed) kck.update(tx_inputs_hash) kck.update(add) return kck.digest()
async def _sign_add_input(state, ctx, msg: BytecoinSignAddInputRequest): if msg.address_index is None: msg.address_index = 0 if msg.amount is None: msg.amount = 0 if state.state != State.EXPECT_ADD_INPUT or state.inputs_counter >= state.inputs_size: raise wire.DataError("Unexpected add_input") state.inputs_amount = misc.add_amount(state.inputs_amount, msg.amount) state.tx_prefix_stream.update(b'\x02') # cn::InputKey::type_tag state.tx_inputs_stream.update(b'\x02') # cn::InputKey::type_tag state.tx_prefix_stream.update(bcncrypto.get_varint_data(msg.amount)) state.tx_inputs_stream.update(bcncrypto.get_varint_data(msg.amount)) state.tx_prefix_stream.update(bcncrypto.get_varint_data(len(msg.output_indexes))) state.tx_inputs_stream.update(bcncrypto.get_varint_data(len(msg.output_indexes))) for index in msg.output_indexes: state.tx_prefix_stream.update(bcncrypto.get_varint_data(index)) state.tx_inputs_stream.update(bcncrypto.get_varint_data(index)) address_audit_secret_key = state.creds.generate_address_secret_key(msg.address_index) inv_output_secret_hash = misc.invert_arg_to_inv_hash(msg.output_secret_hash_arg) output_secret_key_a = inv_output_secret_hash.mul(address_audit_secret_key) output_secret_key_s = inv_output_secret_hash.mul(state.creds.spend_secret_key) output_public_key = misc.secret_keys_to_public_key(output_secret_key_a, output_secret_key_s) keyimage_b = misc.generate_key_image_b(output_public_key, output_secret_key_a) state.tx_prefix_stream.update(keyimage_b) state.tx_inputs_stream.update(keyimage_b) state.inputs_counter += 1 if state.inputs_counter >= state.inputs_size: state.state = State.EXPECT_ADD_OUTPUT state.tx_inputs_hash = state.tx_inputs_stream.digest() state.tx_prefix_stream.update(bcncrypto.get_varint_data(state.outputs_size)) return BytecoinEmptyResponse()
def generate_address_secret_key(self, address_index: int) -> bcncrypto.BcnScalar: if address_index != self.last_address_index: self.last_address_index = address_index sec = bcncrypto.hash_to_scalar( self.A_plus_sH.to_bytes() + b"address" + bcncrypto.get_varint_data(address_index)) self.last_address_audit_secret_key = sec.add( self.audit_key_base_secret_key) return self.last_address_audit_secret_key
def unlinkable_derive_output_public_key( output_secret_point: bcncrypto.BcnPoint, tx_inputs_hash: bytes, output_index: int, address_S: bcncrypto.BcnPoint, address_Sv: bcncrypto.BcnPoint): add = bcncrypto.get_varint_data(output_index) output_secret_hash_s = bcncrypto.hash_to_scalar( output_secret_point.to_bytes() + tx_inputs_hash + add) inv_output_secret_hash_s = output_secret_hash_s.invert() output_public_key = inv_output_secret_hash_s.scalarmult(address_S) encrypted_secret = output_secret_point.add( inv_output_secret_hash_s.scalarmult(address_Sv)) return output_public_key, encrypted_secret
def linkable_derive_output_public_key( output_secret_scalar: bcncrypto.BcnScalar, tx_inputs_hash: bytes, output_index: int, address_S: bcncrypto.BcnPoint, address_V: bcncrypto.BcnPoint): encrypted_secret = output_secret_scalar.scalarmult(address_V) derivation_b = output_secret_scalar.scalarmult_base().to_bytes() add = bcncrypto.get_varint_data(output_index) derivation_hash_scalar = bcncrypto.hash_to_scalar(derivation_b + tx_inputs_hash + add) output_public_key = address_S.add(derivation_hash_scalar.scalarmult_base()) return output_public_key, encrypted_secret
async def _sign_add_output_or_change(state, ctx, amount: int, tag:int, S:bcncrypto.BcnPoint, Sv:bcncrypto.BcnPoint): output_seed = state.creds.generate_output_seed(state.tx_inputs_hash, state.outputs_counter) sca, poi, at = misc.generate_output_secrets(output_seed) output_public_key = None encrypted_secret = None if tag == 0: output_public_key, encrypted_secret = misc.linkable_derive_output_public_key(sca, state.tx_inputs_hash, state.outputs_counter, S, Sv) elif tag == 1: output_public_key, encrypted_secret = misc.unlinkable_derive_output_public_key(poi, state.tx_inputs_hash, state.outputs_counter, S, Sv) else: raise wire.DataError("Unknonw address type") print("ga7") encrypted_address_type = tag ^ at output_public_key_b = output_public_key.to_bytes() encrypted_secret_b = encrypted_secret.to_bytes() state.tx_prefix_stream.update(b'\x02') # cn::OutputKey::type_tag state.tx_prefix_stream.update(bcncrypto.get_varint_data(amount)) state.tx_prefix_stream.update(output_public_key_b) state.tx_prefix_stream.update(encrypted_secret_b) add = bytes([encrypted_address_type]) state.tx_prefix_stream.update(add) state.outputs_counter += 1 if state.outputs_counter >= state.outputs_size: outputs_amount = state.dst_amount outputs_amount = misc.add_amount(outputs_amount, state.change_amount) if outputs_amount > state.inputs_amount: raise wire.DataError("Outputs amount > inputs amount") fee = state.inputs_amount - outputs_amount for key, value in state.dst_amounts.items(): await confirms.require_confirm_output(ctx, key, value) await confirms.require_confirm_fee(ctx, fee) state.state = State.EXPECT_ADD_EXTRA_CHUNK state.tx_prefix_stream.update(bcncrypto.get_varint_data(state.extra_size)) return BytecoinSignAddOutputResponse(public_key=output_public_key_b, encrypted_secret=encrypted_secret_b, encrypted_address_type=encrypted_address_type)
def encrypt_result(self, input:bytes, input_index:int, suffix:bytes): add = bcncrypto.get_varint_data(input_index) k = bcncrypto.cn_fast_hash(self.encryption_key + suffix + add) enc = bytes(a ^ b for a, b in zip(k, input)) return enc
def generate_sign_secret(self, input_index:int, suffix:bytes): add = bcncrypto.get_varint_data(input_index) sec = bcncrypto.hash_to_scalar64(self.random_seed + self.creds.spend_secret_key.to_bytes() + suffix + add) return sec