Move EncryptedTemporaryFile from SFTP frontend to allmydata.util.fileutil, and make the FTP frontend also use it (fixing #1083).

This commit is contained in:
david-sarah 2010-07-11 14:37:21 -07:00
parent c0e7d84c5d
commit 05022dca36
3 changed files with 51 additions and 56 deletions

View File

@ -1,5 +1,4 @@
import tempfile
from zope.interface import implements
from twisted.application import service, strports
from twisted.internet import defer
@ -10,6 +9,7 @@ from twisted.protocols import ftp
from allmydata.interfaces import IDirectoryNode, ExistingChildError, \
NoSuchChildError
from allmydata.immutable.upload import FileHandle
from allmydata.util.fileutil import EncryptedTemporaryFile
class ReadFile:
implements(ftp.IReadFile)
@ -27,7 +27,7 @@ class FileWriter:
raise NotImplementedError("Non-streaming producer not supported.")
# we write the data to a temporary file, since Tahoe can't do
# streaming upload yet.
self.f = tempfile.TemporaryFile()
self.f = EncryptedTemporaryFile()
return None
def unregisterProducer(self):

View File

@ -1,5 +1,5 @@
import os, tempfile, heapq, binascii, traceback, array, stat, struct
import heapq, traceback, array, stat, struct
from types import NoneType
from stat import S_IFREG, S_IFDIR
from time import time, strftime, localtime
@ -32,8 +32,7 @@ from allmydata.interfaces import IFileNode, IDirectoryNode, ExistingChildError,
from allmydata.mutable.common import NotWriteableError
from allmydata.immutable.upload import FileHandle
from allmydata.dirnode import update_metadata
from pycryptopp.cipher.aes import AES
from allmydata.util.fileutil import EncryptedTemporaryFile
noisy = True
use_foolscap_logging = True
@ -288,56 +287,6 @@ def _direntry_for(filenode_or_parent, childname, filenode=None):
return None
class EncryptedTemporaryFile(PrefixingLogMixin):
# not implemented: next, readline, readlines, xreadlines, writelines
def __init__(self):
PrefixingLogMixin.__init__(self, facility="tahoe.sftp")
self.file = tempfile.TemporaryFile()
self.key = os.urandom(16) # AES-128
def _crypt(self, offset, data):
# TODO: use random-access AES (pycryptopp ticket #18)
offset_big = offset // 16
offset_small = offset % 16
iv = binascii.unhexlify("%032x" % offset_big)
cipher = AES(self.key, iv=iv)
cipher.process("\x00"*offset_small)
return cipher.process(data)
def close(self):
self.file.close()
def flush(self):
self.file.flush()
def seek(self, offset, whence=0): # 0 = SEEK_SET
if noisy: self.log(".seek(%r, %r)" % (offset, whence), level=NOISY)
self.file.seek(offset, whence)
def tell(self):
offset = self.file.tell()
if noisy: self.log(".tell() = %r" % (offset,), level=NOISY)
return offset
def read(self, size=-1):
if noisy: self.log(".read(%r)" % (size,), level=NOISY)
index = self.file.tell()
ciphertext = self.file.read(size)
plaintext = self._crypt(index, ciphertext)
return plaintext
def write(self, plaintext):
if noisy: self.log(".write(<data of length %r>)" % (len(plaintext),), level=NOISY)
index = self.file.tell()
ciphertext = self._crypt(index, plaintext)
self.file.write(ciphertext)
def truncate(self, newsize):
if noisy: self.log(".truncate(%r)" % (newsize,), level=NOISY)
self.file.truncate(newsize)
class OverwriteableFileConsumer(PrefixingLogMixin):
implements(IFinishableConsumer)
"""I act both as a consumer for the download of the original file contents, and as a

View File

@ -2,10 +2,13 @@
Futz with files like a pro.
"""
import sys, exceptions, os, stat, tempfile, time
import sys, exceptions, os, stat, tempfile, time, binascii
from twisted.python import log
from pycryptopp.cipher.aes import AES
def rename(src, dst, tries=4, basedelay=0.1):
""" Here is a superkludge to workaround the fact that occasionally on
Windows some other process (e.g. an anti-virus scanner, a local search
@ -112,6 +115,49 @@ class NamedTemporaryDirectory:
if self.cleanup and hasattr(self, 'name'):
rm_dir(self.name)
class EncryptedTemporaryFile:
# not implemented: next, readline, readlines, xreadlines, writelines
def __init__(self):
self.file = tempfile.TemporaryFile()
self.key = os.urandom(16) # AES-128
def _crypt(self, offset, data):
offset_big = offset // 16
offset_small = offset % 16
iv = binascii.unhexlify("%032x" % offset_big)
cipher = AES(self.key, iv=iv)
cipher.process("\x00"*offset_small)
return cipher.process(data)
def close(self):
self.file.close()
def flush(self):
self.file.flush()
def seek(self, offset, whence=0): # 0 = SEEK_SET
self.file.seek(offset, whence)
def tell(self):
offset = self.file.tell()
return offset
def read(self, size=-1):
index = self.file.tell()
ciphertext = self.file.read(size)
plaintext = self._crypt(index, ciphertext)
return plaintext
def write(self, plaintext):
index = self.file.tell()
ciphertext = self._crypt(index, plaintext)
self.file.write(ciphertext)
def truncate(self, newsize):
self.file.truncate(newsize)
def make_dirs(dirname, mode=0777):
"""
An idempotent version of os.makedirs(). If the dir already exists, do