def verify_signature_files(signature_files:Sequence[str], threshold:int=None) -> bool: # Get data from signature share file parties_ids = [] signature_shares = {} master_pubkey = None msg = None derivation_index = None for sig_file in signature_files: in_data = {} try: in_file = open(sig_file, "r") in_data = json.load(in_file) in_file.close() except: raise GenVerErrorBasic(f'Reading key file {sig_file}') try: # Verify same master pubkey curr_master_pubkey = bytes.fromhex(in_data['master_pubkey']) if master_pubkey: if not master_pubkey == curr_master_pubkey: raise GenVerErrorBasic(f'Different master pubkey in different files') else: master_pubkey = curr_master_pubkey # Verify same message curr_msg = in_data['message'] if msg: if not msg == curr_msg: raise GenVerErrorBasic(f'Different messages in different files') else: msg = curr_msg # Verify same derivation indx curr_index = in_data['derivation_index'] if derivation_index: if not derivation_index == curr_index: raise GenVerErrorBasic(f'Different derivation index in different files') else: derivation_index = curr_index curr_id = int(in_data['signer_id']) parties_ids.append(curr_id) signature_shares[curr_id] = bytes.fromhex(in_data['signature']) except: raise GenVerErrorBasic(f'Parsing key file {sig_file}') msg_str = msg['payload'] if msg['hex']: try: msg_bytes = bytes.fromhex(msg['payload']) except: raise GenVerErrorBasic(f'Message string is not hex') else: msg_bytes = bytes(msg_str, 'ascii') # Convert signature shares to G2 elements to later interpolate on G2_signature_shares = {} for id, sig in signature_shares.items(): try: G2_signature_shares[id] = bls_conv.signature_to_G2(sig) except: raise GenVerErrorBasic(f'Invalid encoding of signature for id {id}') if not threshold: threshold = len(parties_ids) der_path = index_to_path(derivation_index) derived_pubkey_address = bls_conv.G1_to_pubkey(derive_public_child(master_pubkey, der_path)) print(f'Verifying signing threshold {threshold} out of {len(parties_ids)} parties') print("Message:", colored(msg_str, "green")) print("Public Key:", colored(derived_pubkey_address.hex(), "green")) # Verify joining threshold singature shares gives a valid signature auth_signature = b'' for auth_ids in itertools.combinations(parties_ids, threshold): auth_signature = bls_conv.G2_to_signature(_interpolate_in_group({id : G2_signature_shares[id] for id in auth_ids}, bls_curve.G2)) if not bls_basic.Verify(derived_pubkey_address, msg_bytes, auth_signature): raise GenVerErrorBasic(f'Failed verification of combined signature for id {auth_ids}.') print("Signature:", colored(auth_signature.hex(), "green")) print(colored("Success!", "green")) return True
def test_verify(pubkey, message, signature, result): assert G2Basic.Verify(pubkey, message, signature) == result
def verify_key_file(key_file:str, passphrase:str, rsa_priv_key_file:str=None): try: in_file = open(key_file, "r") data = json.load(in_file) in_file.close() except: raise GenVerErrorBasic(f'Reading key file {key_file}') try: master_pubkey = bytes.fromhex(data['master_pubkey']) integrity_checksum = bytes.fromhex(data['integrity_checksum']) threshold = data['threshold'] my_id = data['key_id'] parties = data['parties'] encrypted_private_key_share = bytes.fromhex(parties[f'{my_id}']['encrypted_master_private_key_share']) encrypted_integrity_passphrase = bytes.fromhex(parties[f'{my_id}']['encrypted_integrity_passphrase']) parties_ids = [] test_signature_shares = {} for id_str, party in parties.items(): try: id = int(id_str) except: raise GenVerErrorBasic(f'Invalid id {id_str}') parties_ids.append(id) test_signature_shares[id] = bytes.fromhex(party['test_signature_share']) except: raise GenVerErrorBasic(f'Error parsing key file') # If Given RSA private key, use it to get integrity passphrase (if no file, assume integrity passphrase is given) master_private_key_share = None if rsa_priv_key_file is None: integrity_passphrase = bytes(passphrase, 'utf-8') else: try: in_file = open(rsa_priv_key_file, 'r') rsa_key = RSA.importKey(in_file.read(), passphrase=passphrase) cipher = PKCS1_OAEP.new(rsa_key) in_file.close() except: raise GenVerErrorBasic(f'Importing RSA key from file {rsa_priv_key_file} (perhaps wrong passphrase)') if not rsa_key.has_private(): raise GenVerErrorBasic(f'Not a private RSA key file {rsa_priv_key_file}') try: master_private_key_share = cipher.decrypt(encrypted_private_key_share) except: raise GenVerErrorBasic(f'Invalid decryption of private key share from key file') try: integrity_passphrase = cipher.decrypt(encrypted_integrity_passphrase) except: raise GenVerErrorBasic(f'Invalid decryption of integrity passphrase from key file') # Sanity checks if threshold > len(parties_ids) or threshold < 1: raise GenVerErrorBasic(f'Invalid threhsold {threshold} for ids {parties_ids}') if (len(parties_ids) != len(set(parties_ids))): raise GenVerErrorBasic(f'Non-unique ids in verification file') test_derivation_path = _get_test_path() derived_public_key = derive_public_child(master_pubkey, test_derivation_path) derived_pubkey_address = bls_conv.G1_to_pubkey(derived_public_key) test_msg, _ = _get_msg_for_address(derived_pubkey_address) # If decrypted master_private_key Verify my own signature wasn't modified by signing again if master_private_key_share: derived_private_key_share = derive_private_child(master_private_key_share, test_derivation_path, master_pubkey) if not test_signature_shares[my_id] == bls_basic.Sign(derived_private_key_share, test_msg): raise GenVerErrorBasic(f'Modified signature share for my key id {my_id}') else: print(colored('No RSA key - not verifying private key share validity!', "cyan")) # After getting scrypt integrity passphrase validate master pubkey wasn't changed computed_checksum = _compute_scrypt_checksum(integrity_passphrase, master_pubkey) if not computed_checksum == integrity_checksum: raise GenVerErrorBasic(f'Failure in validating master public key integrity checksum (perhaps wrong passphrase)') # Convert to group elements to allow interpolation of signatures G2_signature_shares = {} for id, sig in test_signature_shares.items(): try: G2_signature_shares[id] = bls_conv.signature_to_G2(sig) except: raise GenVerErrorBasic(f'Invalid encoding of signature shares for id {id}') # For each authorized set of the above, combine signature shares and verify print(f'Verifying signing threshold {threshold} out of {len(parties_ids)} parties...') for auth_ids in itertools.combinations(parties_ids, threshold): auth_signature = bls_conv.G2_to_signature(_interpolate_in_group({id : G2_signature_shares[id] for id in auth_ids}, bls_curve.G2)) if not bls_basic.Verify(derived_pubkey_address, test_msg, auth_signature): raise GenVerErrorBasic(f'Failed verification of combined signature for ids {auth_ids}') # Sanity check: check un-authorized set can't get valid signature (less then threhsold) for auth_ids in itertools.combinations(parties_ids, threshold-1): auth_signature = bls_conv.G2_to_signature(_interpolate_in_group({id : G2_signature_shares[id] for id in auth_ids}, bls_curve.G2)) if bls_basic.Verify(derived_pubkey_address, test_msg, auth_signature): raise GenVerErrorBasic(f'Valid signature for unauthorized ids {auth_ids}') print(colored("Success!", "green")) return True