|  | @@ -86,13 +86,12 @@ def _bytestrings_of_length(length):
 | 
	
		
			
				|  |  |      """Generates a stream containing all bytestrings of a given length.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Args:
 | 
	
		
			
				|  |  | -      length: A non-negative integer length.
 | 
	
		
			
				|  |  | +      length: A positive integer length.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Yields:
 | 
	
		
			
				|  |  |        All bytestrings of length `length`.
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      digits = [0] * length
 | 
	
		
			
				|  |  | -    hashes_computed = 0
 | 
	
		
			
				|  |  |      while True:
 | 
	
		
			
				|  |  |          yield b''.join(struct.pack('B', i) for i in digits)
 | 
	
		
			
				|  |  |          digits[-1] += 1
 | 
	
	
		
			
				|  | @@ -108,40 +107,52 @@ def _bytestrings_of_length(length):
 | 
	
		
			
				|  |  |                  digits[i] += 1
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -def _find_secret_of_length(target,
 | 
	
		
			
				|  |  | -                           ideal_distance,
 | 
	
		
			
				|  |  | -                           length,
 | 
	
		
			
				|  |  | -                           stop_event,
 | 
	
		
			
				|  |  | -                           maximum_hashes,
 | 
	
		
			
				|  |  | -                           interesting_hamming_distance=None):
 | 
	
		
			
				|  |  | -    """Find a candidate with the given length.
 | 
	
		
			
				|  |  | +def _all_bytestrings():
 | 
	
		
			
				|  |  | +    """Generates a stream containing all possible bytestrings.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    This generator does not terminate.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Yields:
 | 
	
		
			
				|  |  | +      All bytestrings in ascending order of length.
 | 
	
		
			
				|  |  | +    """
 | 
	
		
			
				|  |  | +    length = 1
 | 
	
		
			
				|  |  | +    while True:
 | 
	
		
			
				|  |  | +        for bytestring in _bytestrings_of_length(length):
 | 
	
		
			
				|  |  | +            yield bytestring
 | 
	
		
			
				|  |  | +        length += 1
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +def _find_secret(target,
 | 
	
		
			
				|  |  | +                 ideal_distance,
 | 
	
		
			
				|  |  | +                 stop_event,
 | 
	
		
			
				|  |  | +                 maximum_hashes,
 | 
	
		
			
				|  |  | +                 interesting_hamming_distance=None):
 | 
	
		
			
				|  |  | +    """Find candidate strings.
 | 
	
		
			
				|  |  | +
 | 
	
		
			
				|  |  | +    Search through the space of all bytestrings, in order of increasing length,
 | 
	
		
			
				|  |  | +    indefinitely, until a hash with a Hamming distance of `maximum_distance` or
 | 
	
		
			
				|  |  | +    less has been found.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Args:
 | 
	
		
			
				|  |  |        target: The search string.
 | 
	
		
			
				|  |  |        ideal_distance: The desired Hamming distance.
 | 
	
		
			
				|  |  | -      length: The length of secret string to search for.
 | 
	
		
			
				|  |  |        stop_event: An event indicating whether the RPC should terminate.
 | 
	
		
			
				|  |  |        maximum_hashes: The maximum number of hashes to check before stopping.
 | 
	
		
			
				|  |  |        interesting_hamming_distance: If specified, strings with a Hamming
 | 
	
		
			
				|  |  |          distance from the target below this value will be yielded.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Yields:
 | 
	
		
			
				|  |  | -      A stream of tuples of type Tuple[Optional[HashNameResponse], int]. The
 | 
	
		
			
				|  |  | -        element of the tuple, if specified, signifies an ideal or interesting
 | 
	
		
			
				|  |  | -        candidate. If this element is None, it signifies that the stream has
 | 
	
		
			
				|  |  | -        ended because an ideal candidate has been found. The second element is
 | 
	
		
			
				|  |  | -        the number of hashes computed up this point.
 | 
	
		
			
				|  |  | +      Instances  of HashNameResponse. The final entry in the stream will be of
 | 
	
		
			
				|  |  | +        `maximum_distance` Hamming distance or less from the target string,
 | 
	
		
			
				|  |  | +        while all others will be of less than `interesting_hamming_distance`.
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      Raises:
 | 
	
		
			
				|  |  |        ResourceLimitExceededError: If the computation exceeds `maximum_hashes`
 | 
	
		
			
				|  |  |          iterations.
 | 
	
		
			
				|  |  |      """
 | 
	
		
			
				|  |  |      hashes_computed = 0
 | 
	
		
			
				|  |  | -    for secret in _bytestrings_of_length(length):
 | 
	
		
			
				|  |  | +    for secret in _all_bytestrings():
 | 
	
		
			
				|  |  |          if stop_event.is_set():
 | 
	
		
			
				|  |  | -            # Yield a sentinel and stop the generator if the RPC has been
 | 
	
		
			
				|  |  | -            # cancelled.
 | 
	
		
			
				|  |  | -            yield None, hashes_computed
 | 
	
		
			
				|  |  |              raise StopIteration()  # pylint: disable=stop-iteration-return
 | 
	
		
			
				|  |  |          candidate_hash = _get_hash(secret)
 | 
	
		
			
				|  |  |          distance = _get_substring_hamming_distance(candidate_hash, target)
 | 
	
	
		
			
				|  | @@ -150,72 +161,19 @@ def _find_secret_of_length(target,
 | 
	
		
			
				|  |  |              yield hash_name_pb2.HashNameResponse(
 | 
	
		
			
				|  |  |                  secret=base64.b64encode(secret),
 | 
	
		
			
				|  |  |                  hashed_name=candidate_hash,
 | 
	
		
			
				|  |  | -                hamming_distance=distance), hashes_computed
 | 
	
		
			
				|  |  | +                hamming_distance=distance)
 | 
	
		
			
				|  |  |          elif distance <= ideal_distance:
 | 
	
		
			
				|  |  | -            # Yield the ideal candidate followed by a sentinel to signal the end
 | 
	
		
			
				|  |  | -            # of the stream.
 | 
	
		
			
				|  |  | +            # Yield ideal candidate and end the stream.
 | 
	
		
			
				|  |  |              yield hash_name_pb2.HashNameResponse(
 | 
	
		
			
				|  |  |                  secret=base64.b64encode(secret),
 | 
	
		
			
				|  |  |                  hashed_name=candidate_hash,
 | 
	
		
			
				|  |  | -                hamming_distance=distance), hashes_computed
 | 
	
		
			
				|  |  | -            yield None, hashes_computed
 | 
	
		
			
				|  |  | +                hamming_distance=distance)
 | 
	
		
			
				|  |  |              raise StopIteration()  # pylint: disable=stop-iteration-return
 | 
	
		
			
				|  |  |          hashes_computed += 1
 | 
	
		
			
				|  |  |          if hashes_computed == maximum_hashes:
 | 
	
		
			
				|  |  |              raise ResourceLimitExceededError()
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  | -def _find_secret(target,
 | 
	
		
			
				|  |  | -                 maximum_distance,
 | 
	
		
			
				|  |  | -                 stop_event,
 | 
	
		
			
				|  |  | -                 maximum_hashes,
 | 
	
		
			
				|  |  | -                 interesting_hamming_distance=None):
 | 
	
		
			
				|  |  | -    """Find candidate strings.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    Search through the space of all bytestrings, in order of increasing length,
 | 
	
		
			
				|  |  | -    indefinitely, until a hash with a Hamming distance of `maximum_distance` or
 | 
	
		
			
				|  |  | -    less has been found.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    Args:
 | 
	
		
			
				|  |  | -      target: The search string.
 | 
	
		
			
				|  |  | -      maximum_distance: The desired Hamming distance.
 | 
	
		
			
				|  |  | -      stop_event: An event indicating whether the RPC should terminate.
 | 
	
		
			
				|  |  | -      maximum_hashes: The maximum number of hashes to check before stopping.
 | 
	
		
			
				|  |  | -      interesting_hamming_distance: If specified, strings with a Hamming
 | 
	
		
			
				|  |  | -        distance from the target below this value will be yielded.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    Yields:
 | 
	
		
			
				|  |  | -      Instances  of HashNameResponse. The final entry in the stream will be of
 | 
	
		
			
				|  |  | -        `maximum_distance` Hamming distance or less from the target string,
 | 
	
		
			
				|  |  | -        while all others will be of less than `interesting_hamming_distance`.
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -    Raises:
 | 
	
		
			
				|  |  | -      ResourceLimitExceededError: If the computation exceeds `maximum_hashes`
 | 
	
		
			
				|  |  | -        iterations.
 | 
	
		
			
				|  |  | -    """
 | 
	
		
			
				|  |  | -    length = 1
 | 
	
		
			
				|  |  | -    total_hashes = 0
 | 
	
		
			
				|  |  | -    while True:
 | 
	
		
			
				|  |  | -        last_hashes_computed = 0
 | 
	
		
			
				|  |  | -        for candidate, hashes_computed in _find_secret_of_length(
 | 
	
		
			
				|  |  | -                target,
 | 
	
		
			
				|  |  | -                maximum_distance,
 | 
	
		
			
				|  |  | -                length,
 | 
	
		
			
				|  |  | -                stop_event,
 | 
	
		
			
				|  |  | -                maximum_hashes - total_hashes,
 | 
	
		
			
				|  |  | -                interesting_hamming_distance=interesting_hamming_distance):
 | 
	
		
			
				|  |  | -            last_hashes_computed = hashes_computed
 | 
	
		
			
				|  |  | -            if candidate is not None:
 | 
	
		
			
				|  |  | -                yield candidate
 | 
	
		
			
				|  |  | -            else:
 | 
	
		
			
				|  |  | -                raise StopIteration()  # pylint: disable=stop-iteration-return
 | 
	
		
			
				|  |  | -            if stop_event.is_set():
 | 
	
		
			
				|  |  | -                # Terminate the generator if the RPC has been cancelled.
 | 
	
		
			
				|  |  | -                raise StopIteration()  # pylint: disable=stop-iteration-return
 | 
	
		
			
				|  |  | -        total_hashes += last_hashes_computed
 | 
	
		
			
				|  |  | -        length += 1
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  | -
 | 
	
		
			
				|  |  |  class HashFinder(hash_name_pb2_grpc.HashFinderServicer):
 | 
	
		
			
				|  |  |  
 | 
	
		
			
				|  |  |      def __init__(self, maximum_hashes):
 |