From 6e8477114e9dfd066021ef89d70bacfc23d0be2c Mon Sep 17 00:00:00 2001 From: Zooko O'Whielacronx Date: Tue, 13 Jul 2010 23:02:55 -0700 Subject: [PATCH] minor code clean-up in dirnode.py Impose micro-POLA by passing only the writekey instead of the whole node object to {{{_encrypt_rw_uri()}}}. Remove DummyImmutableFileNode in nodemaker.py, which is obviated by this. Add micro-optimization by precomputing the netstring of the empty string and branching on whether the writekey is present or not outside of {{{_encrypt_rw_uri()}}}. Add doc about writekey to docstring. fixes #967 --- src/allmydata/dirnode.py | 30 ++++++++++++++++++------------ src/allmydata/nodemaker.py | 11 +++-------- src/allmydata/test/test_dirnode.py | 12 ++++++------ 3 files changed, 27 insertions(+), 26 deletions(-) diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py index 9d41af19..022a6e00 100644 --- a/src/allmydata/dirnode.py +++ b/src/allmydata/dirnode.py @@ -171,12 +171,10 @@ class Adder: new_contents = self.node._pack_contents(children) return new_contents +def _encrypt_rw_uri(writekey, rw_uri): + precondition(isinstance(rw_uri, str), rw_uri) + precondition(isinstance(writekey, str), writekey) -def _encrypt_rw_uri(filenode, rw_uri): - assert isinstance(rw_uri, str) - writekey = filenode.get_writekey() - if not writekey: - return "" salt = hashutil.mutable_rwcap_salt_hash(rw_uri) key = hashutil.mutable_rwcap_key_hash(salt, writekey) cryptor = AES(key) @@ -187,8 +185,7 @@ def _encrypt_rw_uri(filenode, rw_uri): # The MAC is not checked by readers in Tahoe >= 1.3.0, but we still # produce it for the sake of older readers. - -def pack_children(filenode, childrenx, deep_immutable=False): +def pack_children(childrenx, writekey, deep_immutable=False): # initial_children must have metadata (i.e. {} instead of None) children = {} for (namex, (node, metadata)) in childrenx.iteritems(): @@ -196,10 +193,11 @@ def pack_children(filenode, childrenx, deep_immutable=False): "directory creation requires metadata to be a dict, not None", metadata) children[normalize(namex)] = (node, metadata) - return _pack_normalized_children(filenode, children, deep_immutable=deep_immutable) + return _pack_normalized_children(children, writekey=writekey, deep_immutable=deep_immutable) -def _pack_normalized_children(filenode, children, deep_immutable=False): +ZERO_LEN_NETSTR=netstring('') +def _pack_normalized_children(children, writekey, deep_immutable=False): """Take a dict that maps: children[unicode_nfc_name] = (IFileSystemNode, metadata_dict) and pack it into a single string, for use as the contents of the backing @@ -208,9 +206,13 @@ def _pack_normalized_children(filenode, children, deep_immutable=False): as the pre-packed entry, which is faster than re-packing everything each time. + If writekey is provided then I will superencrypt the child's writecap with + writekey. + If deep_immutable is True, I will require that all my children are deeply immutable, and will raise a MustBeDeepImmutableError if not. """ + precondition((writekey is None) or isinstance(writekey, str), writekey) has_aux = isinstance(children, AuxValueDict) entries = [] @@ -231,7 +233,7 @@ def _pack_normalized_children(filenode, children, deep_immutable=False): if rw_uri is None: rw_uri = "" assert isinstance(rw_uri, str), rw_uri - + # should be prevented by MustBeDeepImmutableError check above assert not (rw_uri and deep_immutable) @@ -239,9 +241,13 @@ def _pack_normalized_children(filenode, children, deep_immutable=False): if ro_uri is None: ro_uri = "" assert isinstance(ro_uri, str), ro_uri + if writekey is not None: + writecap = netstring(_encrypt_rw_uri(writekey, rw_uri)) + else: + writecap = ZERO_LEN_NETSTR entry = "".join([netstring(name.encode("utf-8")), netstring(strip_prefix_for_ro(ro_uri, deep_immutable)), - netstring(_encrypt_rw_uri(filenode, rw_uri)), + writecap, netstring(simplejson.dumps(metadata))]) entries.append(netstring(entry)) return "".join(entries) @@ -368,7 +374,7 @@ class DirectoryNode: def _pack_contents(self, children): # expects children in the same format as _unpack_contents returns - return _pack_normalized_children(self._node, children) + return _pack_normalized_children(children, self._node.get_writekey()) def is_readonly(self): return self._node.is_readonly() diff --git a/src/allmydata/nodemaker.py b/src/allmydata/nodemaker.py index 3d0819e8..c852f688 100644 --- a/src/allmydata/nodemaker.py +++ b/src/allmydata/nodemaker.py @@ -8,10 +8,6 @@ from allmydata.dirnode import DirectoryNode, pack_children from allmydata.unknown import UnknownNode from allmydata import uri -class DummyImmutableFileNode: - def get_writekey(self): - return None - class NodeMaker: implements(INodeMaker) @@ -47,7 +43,7 @@ class NodeMaker: # this returns synchronously. It starts with a "cap string". assert isinstance(writecap, (str, type(None))), type(writecap) assert isinstance(readcap, (str, type(None))), type(readcap) - + bigcap = writecap or readcap if not bigcap: # maybe the writecap was hidden because we're in a readonly @@ -97,15 +93,14 @@ class NodeMaker: def create_new_mutable_directory(self, initial_children={}): d = self.create_mutable_file(lambda n: - pack_children(n, initial_children)) + pack_children(initial_children, n.get_writekey())) d.addCallback(self._create_dirnode) return d def create_immutable_directory(self, children, convergence=None): if convergence is None: convergence = self.secret_holder.get_convergence_secret() - n = DummyImmutableFileNode() # writekey=None - packed = pack_children(n, children, deep_immutable=True) + packed = pack_children(children, None, deep_immutable=True) uploadable = Data(packed, convergence) d = self.uploader.upload(uploadable, history=self.history) d.addCallback(lambda results: self.create_from_cap(None, results.uri)) diff --git a/src/allmydata/test/test_dirnode.py b/src/allmydata/test/test_dirnode.py index 14a613ad..d5d01afd 100644 --- a/src/allmydata/test/test_dirnode.py +++ b/src/allmydata/test/test_dirnode.py @@ -1246,33 +1246,33 @@ class Packing(unittest.TestCase): kids = self._make_kids(nm, ["imm", "lit", "write", "read", "dirwrite", "dirread"]) - packed = dirnode.pack_children(fn, kids, deep_immutable=False) + packed = dirnode.pack_children(kids, fn.get_writekey(), deep_immutable=False) self.failUnlessIn("lit", packed) kids = self._make_kids(nm, ["imm", "lit"]) - packed = dirnode.pack_children(fn, kids, deep_immutable=True) + packed = dirnode.pack_children(kids, fn.get_writekey(), deep_immutable=True) self.failUnlessIn("lit", packed) kids = self._make_kids(nm, ["imm", "lit", "write"]) self.failUnlessRaises(dirnode.MustBeDeepImmutableError, dirnode.pack_children, - fn, kids, deep_immutable=True) + kids, fn.get_writekey(), deep_immutable=True) # read-only is not enough: all children must be immutable kids = self._make_kids(nm, ["imm", "lit", "read"]) self.failUnlessRaises(dirnode.MustBeDeepImmutableError, dirnode.pack_children, - fn, kids, deep_immutable=True) + kids, fn.get_writekey(), deep_immutable=True) kids = self._make_kids(nm, ["imm", "lit", "dirwrite"]) self.failUnlessRaises(dirnode.MustBeDeepImmutableError, dirnode.pack_children, - fn, kids, deep_immutable=True) + kids, fn.get_writekey(), deep_immutable=True) kids = self._make_kids(nm, ["imm", "lit", "dirread"]) self.failUnlessRaises(dirnode.MustBeDeepImmutableError, dirnode.pack_children, - fn, kids, deep_immutable=True) + kids, fn.get_writekey(), deep_immutable=True) class FakeMutableFile: implements(IMutableFileNode) -- 2.45.2