diff --git a/common.py b/common.py index 0f4cd0a..8af946c 100644 --- a/common.py +++ b/common.py @@ -2,20 +2,34 @@ import math from hashlib import sha256 SECURITY_PARAMETER = 256 -# Note that `MAXIMUM_HASH_DIFFICULTY` and `SPACE_TO_PROVE_IN_BITS` should be sent from the prover to the verifier before the second verifier initialization step. However to ease this prototype this communication isn't performed. +# Note that notably `MAXIMUM_HASH_DIFFICULTY` and `SPACE_TO_PROVE_IN_BITS` should be sent from the prover to the verifier before the second verifier initialization step. However to ease this prototype this communication isn't performed. # For the ease of this prototype only 1 / (2 ** n) values are supported for `MAXIMUM_HASH_DIFFICULTY`. MAXIMUM_HASH_DIFFICULTY = 1 / 16 # The following space is a minimum. -SPACE_TO_PROVE_IN_BITS = 1_000 +INITIAL_SPACE_TO_PROVE_IN_BITS = 10_000 +SPACE_TO_PROVE_IN_BITS = 10_000 LEAST_DIFFICULT_HASH = int('F' * (SECURITY_PARAMETER // 4), 16) NUMBER_OF_BITS_IN_AVERAGE_FOR_SOLVING_PUZZLE = int(math.log2(1 / MAXIMUM_HASH_DIFFICULTY)) -ENTRIES_NUMBER = math.ceil(SPACE_TO_PROVE_IN_BITS // NUMBER_OF_BITS_IN_AVERAGE_FOR_SOLVING_PUZZLE) + +PROBABILITY_TO_CATCH_MALICIOUS = 1 - 2 ** -1 +# Let's hardcode that we dedicate 1% of extra storage for the proof probability. +SPACE_UPPER_BOUND_IN_BITS_POTENTIALLY_MALICIOUS = math.ceil(SPACE_TO_PROVE_IN_BITS / 100) + +PROBABILITY_NOT_TO_CATCH_MALICIOUS_WITH_ONE_QUERY = (SPACE_TO_PROVE_IN_BITS - SPACE_UPPER_BOUND_IN_BITS_POTENTIALLY_MALICIOUS) / SPACE_TO_PROVE_IN_BITS + +NUMBER_OF_BITS_TO_VERIFY = math.ceil(math.log(1 - PROBABILITY_TO_CATCH_MALICIOUS) / math.log(PROBABILITY_NOT_TO_CATCH_MALICIOUS_WITH_ONE_QUERY)) +NUMBER_OF_ENTRIES_TO_VERIFY = math.ceil(NUMBER_OF_BITS_TO_VERIFY / NUMBER_OF_BITS_IN_AVERAGE_FOR_SOLVING_PUZZLE) + +SPACE_TO_PROVE_IN_BITS = math.ceil(SPACE_TO_PROVE_IN_BITS + SPACE_UPPER_BOUND_IN_BITS_POTENTIALLY_MALICIOUS) + +ENTRIES_NUMBER = math.ceil(SPACE_TO_PROVE_IN_BITS / NUMBER_OF_BITS_IN_AVERAGE_FOR_SOLVING_PUZZLE) # To ease Merkle tree implementation use a power of 2 number of leafs. ENTRIES_NUMBER = 2 ** math.ceil(math.log2(ENTRIES_NUMBER)) LEVELS = int(math.log2(ENTRIES_NUMBER)) + 1 +# Do we have a problem due to length extension attacks? If so, could sha256 one more time as in Bitcoin. def hash(x): return sha256(x.encode('ascii')).hexdigest() diff --git a/prover.py b/prover.py index 6247131..0ef4b43 100755 --- a/prover.py +++ b/prover.py @@ -62,7 +62,8 @@ print(f'{merkleTreeRoot=}') ## Execution phase: 3. Receive a random set of indexes from the verifier. -indexesRequest = ast.literal_eval(input('indexesRequest: ')) +with open(input('indexesRequest: ')) as f: + indexesRequest = ast.literal_eval(f.read()) ## Execution phase: 4. Respond with the corresponding table entries and commitment openings. @@ -103,5 +104,10 @@ for verificationMerkleTreeLevelsIndex, verificationMerkleTreeLevel in enumerate( else: entries += [otherHash] -print(f'{entries=}') +entriesFilePath = 'entries.txt' + +with open(entriesFilePath, 'w') as f: + f.write(str(entries)) + +print(f'entries: {entriesFilePath}') diff --git a/verifier.py b/verifier.py index 078931d..7f6e9b9 100755 --- a/verifier.py +++ b/verifier.py @@ -4,11 +4,6 @@ import secrets, common, ast -# Propose a probability of confidence setting is left for future work. -# Is actually a fraction giving a constant probability of confidence no matter the number of entries? I mean doesn't a constant number of entries to verify doesn't provide such a constant probability even for various number of entries? -# Have to address this scalability issue. -FRACTION_OF_SPACE_TO_VERIFY = 10 ** -2 - ## Initialization phase: Generate a random bitstring to send to the prover. # To make sure that the following protocol execution can't be used partially or totally by the prover to reduce the cost of another protocol execution. @@ -35,11 +30,16 @@ merkleTreeRoot = input('merkleTreeRoot: ') indexesRequest = set() -while len(indexesRequest) < common.ENTRIES_NUMBER * FRACTION_OF_SPACE_TO_VERIFY: +while len(indexesRequest) < common.NUMBER_OF_ENTRIES_TO_VERIFY: index = secrets.randbelow(common.ENTRIES_NUMBER) indexesRequest.add(index) -print(f'{indexesRequest=}') +indexesRequestFilePath = 'indexesRequest.txt' + +with open(indexesRequestFilePath, 'w') as f: + f.write(str(indexesRequest)) + +print(f'indexesRequest: {indexesRequestFilePath}') ## Execution phase: 4. Receive and verify corresponding table entries and commitment openings. @@ -48,7 +48,8 @@ print(f'{indexesRequest=}') # Proceeding in a minimum number of passes could be interesting but this can be left for future work. -entries = ast.literal_eval(input('entries: ')) +with open(input('entries: ')) as f: + entries = ast.literal_eval(f.read()) merkleTreeLevels = [{} for _ in range(common.LEVELS)] @@ -58,7 +59,7 @@ for index in indexesRequest: for i, nonce in zip([leftIndex, rightIndex], [entries.pop(0) for _ in range(2)]): hashed = common.hash(protocolInitializationPhaseId + str(nonce)) if not common.doesHashMatchDifficulty(hashed): - print("The received difficulty doesn't match the claimed one!'") + print("The received difficulty doesn't match the claimed one!") exit(1) merkleTreeLevels[0][i] = common.hash(protocolInitializationPhaseId + protocolExecutionPhaseId + str(nonce)) @@ -76,8 +77,7 @@ for merkleTreeLevelsIndex, merkleTreeLevel in enumerate(merkleTreeLevels[:-1]): actualStorage = common.ENTRIES_NUMBER * common.NUMBER_OF_BITS_IN_AVERAGE_FOR_SOLVING_PUZZLE if merkleTreeRoot == merkleTreeLevels[-1][0] and actualStorage >= common.SPACE_TO_PROVE_IN_BITS: - # Could precise the probability. - print(f'Verified with some probability that the prover is storing in average {common.SPACE_TO_PROVE_IN_BITS} (actually the prover is storing in average {actualStorage} bits)') + print(f'Verified with probability {common.PROBABILITY_TO_CATCH_MALICIOUS} that the prover is storing in average {common.INITIAL_SPACE_TO_PROVE_IN_BITS} (actually the prover is storing in average {actualStorage} bits)') else: print("The prover hasn't provided a correct proof!")