mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-04-09 20:01:23 +00:00
Move EncryptedTemporaryFile from SFTP frontend to allmydata.util.fileutil, and make the FTP frontend also use it (fixing #1083).
This commit is contained in:
parent
c0e7d84c5d
commit
05022dca36
@ -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):
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user