Skip to content

Commit

Permalink
parsed verificaion, need to verify
Browse files Browse the repository at this point in the history
  • Loading branch information
udi0peled committed Nov 27, 2020
1 parent d26f9d4 commit 7456f14
Show file tree
Hide file tree
Showing 6 changed files with 160 additions and 26 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
8a520e2b2252d2324dcf1f8ad1b38ed3a5a911a6b2e0cecb1c0774c42c873cd6e97fd93b7e794f3f93822a4e5dd24be2
1
8a520e2b2252d2324dcf1f8ad1b38ed3a5a911a6b2e0cecb1c0774c42c873cd6e97fd93b7e794f3f93822a4e5dd24be2
983cf67e18da6491185e19ee4445af3098796520fc408cbc99fb7003f290f0a002ee5e0d6c9537b8f665ded3dffd08680bd6aefb3f118964a44af5bf5e2b2852b23ae116ef6b88942dc77de8b8e1ae1d93af326508e4c889219c5d4d8f8cd253
2 changes: 1 addition & 1 deletion fb_bls_key_generator.py → fb_bls_generate_key.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def query_yes_no(question, default="yes"):
def main():
parser = argparse.ArgumentParser() #formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("RSA_public_keys", type=str, nargs="+", help="space seperated list of RSA public key files")
parser.add_argument("-t", "--threshold", type=int, help="minimal number of shares able to reconstruct private key")
parser.add_argument("-t", "--threshold", type=int, help="minimal number of shares able to reconstruct private key (if none, assume all)")
args = parser.parse_args()

# Set party ids for each RSA key file (allows duplicate, will get different shares+id)
Expand Down
File renamed without changes.
74 changes: 74 additions & 0 deletions fb_bls_verify_shares.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import sys
import os
import argparse
import getpass
import sys
from termcolor import colored

from utils import recover
from utils import genver

pubkey_descriptions = {
'MPC_ECDSA_SECP256K1': 'MPC_ECDSA_SECP256K1 XPUB',
'MPC_EDDSA_ED25519': 'MPC_EdDSA_ED25519 extended public key (Fireblocks format)',
'MPC_BLS_BLS12381': 'MPC_BLS_BLS12381 extended public key (Fireblocks format)',
}

privkey_descriptions = {
'MPC_ECDSA_SECP256K1': 'MPC_ECDSA_SECP256K1 XPRV',
'MPC_EDDSA_ED25519': 'MPC_EdDSA_ED25519 extended private key (Fireblocks format)',
'MPC_BLS_BLS12381': 'MPC_BLS_BLS12381 extended private key (Fireblocks format)',
}

def query_yes_no(question, default="yes"):
"""Ask a yes/no question via input() and return their answer.
"question" is a string that is presented to the user.
"default" is the presumed answer if the user just hits <Enter>.
It must be "yes" (the default), "no" or None (meaning
an answer is required of the user).
The "answer" return value is True for "yes" or False for "no".
"""
valid = {"yes": True, "y": True, "ye": True,
"no": False, "n": False}
if default is None:
prompt = " [y/n] "
elif default == "yes":
prompt = " [Y/n] "
elif default == "no":
prompt = " [y/N] "
else:
raise ValueError("invalid default answer: '%s'" % default)

while True:
sys.stdout.write(question + prompt)
choice = input().lower()
if default is not None and choice == '':
return valid[default]
elif choice in valid:
return valid[choice]
else:
sys.stdout.write("Please respond with 'yes' or 'no' "
"(or 'y' or 'n').\n")

def main():
parser = argparse.ArgumentParser() #formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("verification_files",type=str, nargs="+", help="space seperated list of signature share files generates by each party")
parser.add_argument("-t", "--threshold", type=int, help="minimal number of shares able to reconstruct private key (if none, assume all)")
parser.add_argument("-a", "--address", type=str, help="BLS public key address (if none, deduce from share files)")
args = parser.parse_args()

for sig_file in args.verification_files:
if not os.path.exists(sig_file):
print(f'Signature Share Verificaion file {sig_file} not found.')
exit(-1)

genver.verify_signature_shares(args.verification_files, args.threshold, args.address)

if __name__ == "__main__":
main()
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
a0febe7768f95372c0a965b62dd6dc8dcae63b3f3e17cd8ccc398d638b701fd94183fa1cf40f7d995f093f50a0a37c28b22312b20543c6effe7591475b195c4526de2c21ec3d767759579f53922c1327656d06a59f47306fe64034b1f8cff25442fa1e20294e542cc940cc72b1989fb910f9ab7c1a2852886f6b4a9bed782a59d13b402e40db5df961be081c45cbc30c61a9b3e09a1c57db537acd7d64d38b6fd2bebf3909cab47578daa00e3bc1d0a527a9d5012c8abc8c8428d6c5a7e0528f96b8c17f0c39976d8f444e32ce73fe1fb599609f3326bfccc6dfe0ff84a5ba65aa8e3da06b0a305209bea25876263e3d3fce134996e8f3707415d3d68c22b36354ad99f9a6208e41fea2166545dc60539771360e456b884d732726552ef2786104471083d692e862aaff6273a0fe18c56d39e3dbdb580606818d20772669cf347e52ea17c0f6a7193ace6ea9d4e3d174339be9a3804fcd0c8f5524ad317f8c119061e8320dec6f79cd6d104a24a29c05adb93a1b62b75791412f6ad9aa25efca54ed7dff603e94ff45b05987864f19dc2e1a2a0f686dbc079bb0b99c2a383e2e57ca47a112b755dffa6445716f5b0636008f554d389d1295b250c4409b531a97cffc97b30e7dc3a7e240f2b45ecfa0087ea9d3494bb1db6353bd4ce62345e87e3f78f7d3ddc18cb3ab3999861a82ff528f69ed72d3a8dafe38a808f3aa98e404
105 changes: 80 additions & 25 deletions utils/genver.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,21 +23,6 @@ def __init__(self, msg):
def __str__(self):
return f'ERROR: {self._msg}'

class GenVerErrorInvalidPublicKeyReconstructed(GenVerErrorBasic):
pass

class GenVerErrorRSAKeyImport(GenVerErrorBasic):
pass

class GenVerErrorBLSKeyImport(GenVerErrorBasic):
pass

class GenVerErrorRSAEncryption(GenVerErrorBasic):
pass

class GenVerErrorRSADecryption(GenVerErrorBasic):
pass

#bls_opt.curve_order = 7
# Raise error if randomness is too short
def sample_random_in_range(range:int):
Expand Down Expand Up @@ -122,7 +107,7 @@ def generate_bls12381_private_shares(rsa_key_files:Dict[int,str], threshold:int,
curr_public_key = interpolate_public({id : public_shares[id] for id in auth_ids})
print(curr_public_key)
if not bls_opt.eq(public_key, curr_public_key):
raise GenVerErrorInvalidPublicKeyReconstructed(f'Invalid public key for parties {auth_ids}')
raise GenVerErrorBasic(f'Invalid public key for parties {auth_ids}')

pubkey_address = bls_conv.G1_to_pubkey(public_key)
pubkey_address_hex = pubkey_address.hex()
Expand All @@ -132,7 +117,7 @@ def generate_bls12381_private_shares(rsa_key_files:Dict[int,str], threshold:int,
rsa_key = RSA.importKey(open(rsa_key_file, 'r').read())
cipher = PKCS1_OAEP.new(rsa_key)
except Exception as e:
raise GenVerErrorRSAKeyImport(f'Reading RSA key file {rsa_key_file}')
raise GenVerErrorBasic(f'Reading RSA key file {rsa_key_file}')

# Encode and encrypt: id, private share, (combined) public address
plaintext = bytearray(id.to_bytes(8, byteorder="big"))
Expand All @@ -142,50 +127,56 @@ def generate_bls12381_private_shares(rsa_key_files:Dict[int,str], threshold:int,
ciphertext = cipher.encrypt(plaintext)
out_filename = f'id_{id}_bls_private_key_share_address_{pubkey_address_hex}.rsa_enc'
if os.path.exists(out_filename):
raise GenVerErrorRSAEncryption(f'Will not write on existing file {out_filename}')
raise GenVerErrorBasic(f'Will not write on existing file {out_filename}')

try:
out_file = open(out_filename, "w+")
out_file.write(f'{ciphertext.hex()}')
out_file.close()
except Exception as e:
raise GenVerErrorRSAEncryption(f'Error writing encrypted private key share for id {id}')
raise GenVerErrorBasic(f'Error writing encrypted private key share for id {id}')
import math

#TODO return address (instead of public key)
return pubkey_address

test_message = b'BLS MPC Signing: Fireblocks Approves This Message!'

def sign_with_share(rsa_key_file, bls_key_share_file, rsa_key_pass=None):
# Generate signature share of test_message, using bls_key (rsa encrypted with given private key, and perhapse passphrase)
# Write verification data: id, pubkey share, pubkey address, signature share to verification file
# Later, with threshold amount of such verificaion files, should be able to reconstruct and verity bls signature on test_message
def sign_with_share(rsa_key_file:Sequence[str], bls_key_share_file:Sequence[str], rsa_key_pass:str=None):
try:
in_file = open(rsa_key_file, 'r')
rsa_key = RSA.importKey(in_file.read(), passphrase=rsa_key_pass)
cipher = PKCS1_OAEP.new(rsa_key)
in_file.close()
except ValueError:
raise GenVerErrorRSAKeyImport(f'Reading RSA key file {rsa_key_file}')
raise GenVerErrorBasic(f'Reading RSA key file {rsa_key_file}')

if not rsa_key.has_private():
raise GenVerErrorRSAKeyImport(f'Not a private RSA key file {rsa_key_file}')
raise GenVerErrorBasic(f'Not a private RSA key file {rsa_key_file}')

try:
in_file = open(bls_key_share_file, "r")
encrypted_data = bytearray.fromhex(in_file.read())
in_file.close()
except ValueError:
raise GenVerErrorBLSKeyImport(f'Reading BLS key file {bls_key_share_file}')
raise GenVerErrorBasic(f'Reading BLS key file {bls_key_share_file}')

try:
plaintext = cipher.decrypt(encrypted_data)
id = int.from_bytes(plaintext[:8], byteorder="big")
priv_key_share = int.from_bytes(plaintext[8:8+32], byteorder="big")
pubkey_address = plaintext[8+32:8+32+48]
except ValueError:
raise GenVerErrorRSADecryption(f'Error RSA-decrypting BLS key file {bls_key_share_file} using {rsa_key_file}')
raise GenVerErrorBasic(f'RSA-decrypting BLS key file {bls_key_share_file} using {rsa_key_file}')

public_key_share = bls_basic.SkToPk(priv_key_share)

print(f'id: {id}')
print(f'priv key share: {priv_key_share}')
print(f'public key share: {public_key_share.hex()}')

test_msg = bytearray(test_message)
test_msg.extend(pubkey_address)
Expand All @@ -194,11 +185,75 @@ def sign_with_share(rsa_key_file, bls_key_share_file, rsa_key_pass=None):
sig_ver_file_name = f'bls_signature_verification_id_{id}_address_{pubkey_address.hex()}.txt'
try:
out_file = open(sig_ver_file_name, "w+")
out_file.write(f'{pubkey_address.hex()}\n')
out_file.write(f'{id}\n')
out_file.write(f'{public_key_share.hex()}\n')
out_file.write(f'{signature_share.hex()}')
out_file.close()
except Exception as e:
raise GenVerErrorSignatureShare(f'Can\'t write signature share file {sig_ver_file}')
raise GenVerErrorBasic(f'Writing signature share file {sig_ver_file_name}')

# Verify eahc threshold of signature shares from given files can reconstruct verifiable signature on test_message
# If threshold not given, assume all files
# Check sane pubkey in all verificaion files. If None, set from first verification file
def verify_signature_shares(sig_share_files: Sequence[str], threhsold:int=None, public_key_address:str=None):
if not threhsold:
threhsold = len(sig_share_files)

# convert pubkey string to group element
if public_key_address:
try:
pubkey = bls_conv.pubkey_to_G1(bytearray.fromhex(public_key_address))
bls_conv.pubkey_subgroup_check(pubkey)
except Exception as e:
raise GenVerErrorBasic(f'Invalid public key address {public_key_address}')

sig_shares = dict()
public_shares = dict()
for sig_file in sig_share_files:
try:
in_file = open(sig_file, "r")
in_data = in_file.read().splitlines()
in_file.close()
except Exception as e:
raise GenVerErrorBasic(f'Reading verificaion file {sig_file}')

# Check file's public key is same as given (or set it if n/a)
if not public_key_address:
public_key_address = in_data[0]

if public_key_address != in_data[0]:
raise GenVerErrorBasic(f'Different public key address in file {sig_file}')

# Get id
try:
id = int(in_data[1])
except ValueError:
raise GenVerErrorBasic(f'Invalid share id {in_data[1]} in file {sig_file}')

if id in public_shares.keys():
raise GenVerErrorBasic(f'Duplicate id {id} in file {sig_file}')

curr_public_key_share = in_data[2]
try:
curr_public_G1_share = bls_conv.pubkey_to_G1(bytearray.fromhex(curr_public_key_share))
bls_conv.pubkey_subgroup_check(curr_public_G1_share)
except Exception as e:
raise GenVerErrorBasic(f'Invalid public key share address {curr_public_key_share} in file {sig_file}')

public_shares[id] = curr_public_G1_share

curr_signature_share = in_data[3]
try:
curr_sig_G2_share = bls_conv.signature_to_G2(bytearray.fromhex(curr_signature_share))
bls_conv.subgroup_check(curr_sig_G2_share)
except Exception as e:
raise GenVerErrorBasic(f'Invalid signature share in file {sig_file}')

sig_shares[id] = curr_sig_G2_share

# Get public key share


#TODO verify_data(address, verification_file_list, RSA_key_file, bls_key_share_file)
#TODO read all data in verification files (values, check same data: threshold, signature_share, public)
Expand Down

0 comments on commit 7456f14

Please sign in to comment.