commit 04c334d0fc3613961e3e1315ed906de2817b5b9e Author: Damian Johnson atagar@torproject.org Date: Thu Nov 29 11:09:53 2018 -0800
Calculate network status document digests
Adding a digest() method to our base NetworkStatusDocument class. The spec is vague on how this is calculated (#28664), but through experimentation figured out that the range is up through the first 'directory-signature ' including the following space.
To determine this I fetched the detached signatures and upcoming consensus during the voting period (minutes 55-60 of the hour), and compared our calculated result with that...
============================================================ demo.py script ============================================================
import stem.descriptor
desc = next(stem.descriptor.parse_file( '/home/atagar/Desktop/next_consensus', descriptor_type = 'network-status-consensus-3 1.0', document_handler = stem.descriptor.DocumentHandler.DOCUMENT), )
print('digest: %s' % desc.digest())
============================================================
% curl http://128.31.0.39:9131/tor/status-vote/next/consensus-signatures > next_sigs % curl http://128.31.0.39:9131/tor/status-vote/next/consensus > consensus
% grep consensus-digest sigs consensus-digest 296BA01987256A1C8EFB20E17667152DCFA50755
% python demo.py digest: 296BA01987256A1C8EFB20E17667152DCFA50755 --- stem/descriptor/networkstatus.py | 28 +++++++++++++++++++++++ test/unit/descriptor/networkstatus/document_v3.py | 1 + 2 files changed, 29 insertions(+)
diff --git a/stem/descriptor/networkstatus.py b/stem/descriptor/networkstatus.py index 8cf07f03..45054b6b 100644 --- a/stem/descriptor/networkstatus.py +++ b/stem/descriptor/networkstatus.py @@ -68,6 +68,8 @@ import stem.version from stem.descriptor import ( PGP_BLOCK_END, Descriptor, + DigestHash, + DigestEncoding, TypeAnnotation, DocumentHandler, _descriptor_content, @@ -421,6 +423,32 @@ class NetworkStatusDocument(Descriptor): Common parent for network status documents. """
+ def digest(self, hash_type = DigestHash.SHA1, encoding = DigestEncoding.HEX): + """ + Digest of this descriptor's content. These are referenced by... + + * **DetachedSignature** + + * Referer: :class:`~stem.descriptor.networkstatus.DetachedSignature` **consensus_digest** attribute + * Format: **SHA1/HEX** + + .. versionadded:: 1.8.0 + + :param stem.descriptor.DigestHash hash_type: digest hashing algorithm + :param stem.descriptor.DigestEncoding encoding: digest encoding + + :returns: **hashlib.HASH** or **str** based on our encoding argument + """ + + content = self._content_range(end = '\ndirectory-signature ') + + if hash_type == DigestHash.SHA1: + return stem.descriptor._encode_digest(hashlib.sha1(content), encoding) + elif hash_type == DigestHash.SHA256: + return stem.descriptor._encode_digest(hashlib.sha256(content), encoding) + else: + raise NotImplementedError('Network status document digests are only available in sha1 and sha256, not %s' % hash_type) +
def _parse_version_line(keyword, attribute, expected_version): def _parse(descriptor, entries): diff --git a/test/unit/descriptor/networkstatus/document_v3.py b/test/unit/descriptor/networkstatus/document_v3.py index 92100141..daa88a09 100644 --- a/test/unit/descriptor/networkstatus/document_v3.py +++ b/test/unit/descriptor/networkstatus/document_v3.py @@ -120,6 +120,7 @@ ci356fosgLiM1sVqCUkNdA== self.assertEqual(expected_bandwidth_weights, document.bandwidth_weights) self.assertEqual([], document.consensus_methods) self.assertEqual(None, document.published) + self.assertEqual('270D2E02D8E6AD83DD87BD56CF8B7874F75063A9', document.digest()) self.assertEqual([], document.get_unrecognized_lines()) self.assertEqual('@type network-status-consensus-3 1.0', str(document.type_annotation()))