Compare commits

..

18 Commits
0.1 ... py3

Author SHA1 Message Date
shim_
69578e4281 laid base for expiring links 2018-10-04 18:05:23 +02:00
shim_
2344c7b8af better api support 2018-10-04 18:02:13 +02:00
shim_
3bcccd4092 this is why it should have been written in a proper language 2018-10-04 17:06:09 +02:00
shim_
70e7814894 address double slash issue 2018-10-04 17:05:50 +02:00
shim_
48c89f2c71 check connectivty before query 2018-10-04 16:51:10 +02:00
shim_
43e4d4cdf9 impl. auth ping 2018-10-04 16:50:49 +02:00
shim_
02fdcbcb82 publish package 2018-10-03 16:46:00 +02:00
shim_
2c32e74fd3 fix seafapi.ping 2018-10-03 15:34:41 +02:00
shim_
5a4fbed69f use global session 2018-10-03 14:07:26 +02:00
shim_
ecc1cc667b implemented derived passwords 2018-07-21 14:02:15 +02:00
shim_
0c803ba912 fuck finally 2018-07-19 23:18:43 +02:00
shim_
4a41f768c2 Python3 was a mistake 2018-07-19 22:53:07 +02:00
shim_
d6367a72cf removed unicode conversions 2018-07-19 22:27:45 +02:00
shim_
e26d5690cf don't reset host after loading config -_- 2018-07-19 22:24:14 +02:00
shim_
894f15ec85 py3 print 2018-07-19 22:19:59 +02:00
shim_
4874c8e119 removed scrypt dependency 2018-07-19 22:12:06 +02:00
shim_
d7dbff5f28 requests depedency 2018-07-19 22:09:41 +02:00
shim_
da7d873beb downgrade py version for compatibility 2018-07-19 22:06:53 +02:00
8 changed files with 130 additions and 56 deletions

View File

@ -1,9 +1,17 @@
pipeline:
modules:
image: python:3.6
image: python:3.5
commands:
- ./make-modules.sh
package:
image: kramos/alpine-zip
commands:
- zip seafile.zip -r modules icon.png main.py metadata.xml settings.ui
upload_package:
image: vividboarder/drone-webdav
file: seafile.zip
destination: https://git.shimun.net/files/screencloud-seafile/$$TAG.zip
username: git
secrets: [webdav_password]
when:
event: tag

View File

@ -1,21 +1,20 @@
import bson, scrypt
import bson#, scrypt
from Crypto.Cipher import AES
from Crypto.Util import Counter
from Crypto.PublicKey import RSA
from Crypto.PublicKey import DSA
from salsa20 import Salsa20_keystream
import os, struct, time, hashlib, hashlib, random
import os, struct, time, hashlib, hashlib, random, binascii
class EncryptedScreenshot:
def __init__(self, metadata,id=None,signer=None,password_encryptor=None):
def __init__(self, metadata,password=None,id=None,signer=None,password_encryptor=None):
def rand(len):
return ''.join(
random.choice("1234567890ABCDEFGHIJKLMNOPQRSTUWVXYZabcdefghijklmnopqrstuwvxyz") for _ in range(len))
self.password = rand(16)
print("Passphrase %s" % str(bytearray(self.passphrase())).encode("hex"))
self.password = (password or rand(16))
self.id = id
if id is None:
self.id = rand(8)
@ -34,7 +33,7 @@ class EncryptedScreenshot:
# new ScryptParameters(64, 8, 1,32, new Uint8List.fromList(new List<int>()))
print("Password units: %s" % (map(ord, self.password.encode("utf-8")),))
sha = hashlib.sha256()
sha.update(self.password)
sha.update(self.password.encode("utf-8"))
return sha.digest() # scrypt.hash(self.password.encode("utf-8"), '', 64, 8, 1, 32)
def assemble(self, file):
@ -42,25 +41,26 @@ class EncryptedScreenshot:
self.metadata["hash"] = image_digest
unencrypted_metadata = bson.dumps(self.metadata)
if len(unencrypted_metadata) % 16 != 0:
unencrypted_metadata += ' ' * (16 - len(unencrypted_metadata) % 16)
unencrypted_metadata += b' ' * (16 - len(unencrypted_metadata) % 16)
(encryptor, iv) = self.encryptor(len(unencrypted_metadata))
encrypted_metadata = []
encrypted_metadata = b''
encrypted_metadata += encryptor(unencrypted_metadata)
encrypted_metadata = iv + str(bytearray(encrypted_metadata))
encrypted_metadata = iv + encrypted_metadata
print("Metadata: %s" % str(encrypted_metadata).encode("base64").replace("\n", ""))
print("%s %s" % (str(encrypted_metadata[:16]).encode("hex"), str(encrypted_metadata[16:]).encode("hex")))
print("Unencrypted: %s" % (unencrypted_metadata.encode("hex")))
print("Password %s" % self.password)
#print("Metadata: %s" % str(encrypted_metadata).encode("base64").replace("\n", ""))
#print("%s %s" % (str(encrypted_metadata[:16]).encode("hex"), str(encrypted_metadata[16:]).encode("hex")))
#print("Unencrypted: %s" % (unencrypted_metadata.encode("hex")))
#print("Password %s" % self.password)
print(bson.loads(unencrypted_metadata))
#print(bson.loads(unencrypted_metadata))
fields = {
"image": encrypted_image,
"metadata_encryption": self.metadata_encryption,
"metadata": encrypted_metadata if self.metadata_encryption else self.metadata
"metadata": encrypted_metadata if self.metadata_encryption else self.metadata,
"public_metadata": self.metadata["public"]
}
if self.signer is not None:
@ -73,8 +73,9 @@ class EncryptedScreenshot:
def encryptor(self,length=0):
iv = os.urandom(16)
ctr = Counter.new(128, initial_value=long(iv.encode("hex"), 16))
print("IV: %s" % iv.encode("hex"))
nonce = int(binascii.hexlify(iv), 16)
ctr = Counter.new(128,initial_value = nonce)# initial_value = nonce)
print("IV: %s" % binascii.hexlify(iv))
cipher = AES.new(self.passphrase(), AES.MODE_CTR, counter=ctr)
#salsa
@ -94,7 +95,7 @@ class EncryptedScreenshot:
def encrypt(self, file):
filesize = os.path.getsize(file)
(encryptor, iv) = self.encryptor(filesize)
binary = []
binary = b''
digest = hashlib.sha256()
with open(file, 'rb') as infile:
# binary += struct.pack('<Q', filesize)
@ -106,7 +107,7 @@ class EncryptedScreenshot:
pass # chunk += ' ' * (16 - len(chunk) % 16)
digest.update(chunk)
binary += encryptor(chunk)
return (digest.digest(), iv + str(bytearray(binary)))
return (digest.digest(), iv + binary)
from Crypto.Signature import PKCS1_v1_5
from Crypto.Hash import SHA256
@ -130,8 +131,8 @@ class Signer:
def signature(self,data):
signed = self.sign(data)
return {"signed-hash": signed,
"signature-algorithm": "SHA-256/%s" % unicode("RSA" if self.mode == RSA else "DSA"),
"key-id": unicode(self.privateKeyId)
"signature-algorithm": "SHA-256/%s" % ("RSA" if self.mode == RSA else "DSA"),
"key-id": (self.privateKeyId)
}

30
imports/hashderive.py Normal file
View File

@ -0,0 +1,30 @@
import hashlib
import binascii
class HashDerivedKey:
@staticmethod
def from_hex(hex_str):
return HashDerivedKey(bytes(bytearray.fromhex(hex_str)))
def __init__(self, master):
def ensure_bytes(b):
assert isinstance(b, bytes)
return b
ensure_bytes(master)
def derive(seed):
ensure_bytes(seed)
sha = hashlib.sha256()
sha.update(master)
sha.update(seed)
return sha.digest()
hex = lambda b: binascii.hexlify(b).decode("utf-8")
self.master = master
self.master_hex = lambda: hex(master)
self.derive = derive
self.derive_hex = lambda seed: hex(derive(seed))
def derive_metadata(self,seed):
key = self.derive(seed)
return (key,{"key_seed": binascii.hexlify(seed).decode("utf-8")})

View File

@ -1,7 +1,8 @@
import time
from encryptedscreenshot import EncryptedScreenshot, Signer
import getpass, os
from hashderive import HashDerivedKey
import getpass, os, sys
from PythonQt.QtGui import QInputDialog
from PythonQt.QtCore import QSettings
from seafapi import *
@ -36,14 +37,17 @@ class EncryptedProcessor(Processor):
def __init__(self,seaf_lib,lib_path):
self.seaf_lib = seaf_lib
self.lib_path = lib_path
self.host = None
self.derived_key = HashDerivedKey(os.urandom(32))
self.load_settings()
self.host = ""
def load_settings(self):
settings = QSettings()
settings.beginGroup("uploaders")
settings.beginGroup("seafile")
self.host = settings.value("encscreen-url", "")
self.host = settings.value("encscreen-url", None)
if settings.value("encscreen-derived-key", None):
self.derived_key = HashDerivedKey.from_hex(settings.value("encscreen-derived-key", None))
settings.endGroup()
settings.endGroup()
@ -52,6 +56,7 @@ class EncryptedProcessor(Processor):
settings.beginGroup("uploaders")
settings.beginGroup("seafile")
settings.setValue("encscreen-url", self.host)
if self.derived_key: settings.setValue(self.derived_key.master_hex())
settings.endGroup()
settings.endGroup()
@ -59,17 +64,28 @@ class EncryptedProcessor(Processor):
return self.host is not None and self.host != ""
def configure(self,parent):
self.host = QInputDialog.getText(parent, 'Encscreen Server Setup', 'Enter server url (ex. https://servertld/s#%id%key):', text="https://screens.shimun.net/s#%id%key")
self.host = QInputDialog.getText(parent, 'Encscreen Server Setup', 'Enter server url (ex. https://servertld/s#%id%key):', text=(self.host or "https://screens.shimun.net/s#%id%key"))
master = QInputDialog.getText(parent, 'Encscreen Master Key Setup', 'Enter master key (hex encoded):', text=(self.derived_key.master_hex() if self.derived_key else "<random>"))
try:
self.derived_key = HashDerivedKey.from_hex(master)
except:
self.derived_key = HashDerivedKey(os.urandom(32))
self.save_settings()
def upload(self,file,name):
enrypted = EncryptedScreenshot({
"owner": unicode(getpass.getuser()),
"format": unicode(str(file).split('.')[-1]),
"title": unicode(name),
def derive():
seed = os.urandom(16)
(_, key_meta) = self.derived_key.derive_metadata(seed)
return (self.derived_key.derive_hex(seed)[0:16], key_meta)
(key, key_meta) = derive()
enrypted = EncryptedScreenshot(metadata = {
"owner": getpass.getuser(),
"format": str(file).split('.')[-1],
"title": name,
"timestamp": int(time.time() * 1000),
"size": os.stat(file).st_size
},signer=Signer.default())
"size": os.stat(file).st_size,
"public": key_meta
},password=key,signer=Signer.default())
tmpHandle = open(file + "c", 'wb')
tmpHandle.write(enrypted.assemble(file))
tmpHandle.close()

View File

@ -6,14 +6,17 @@ class SeafileClient:
def __init__(self,server,username,password=None,token=None):
self.server = server
self.token = token
self.session = requests.Session()
if token:
self.session.headers.update({'Authorization': "Token %s" % self.token.token})
self.login = (username,password)
def api_endpoint(self):
return "%s/api2/" % self.server
return "%s/api2" % self.server
def ping(self):
def ping(self,auth=False):
try:
return requests.get("%s/ping" % self.api_endpoint()).text == "pong"
return self.session.get("%s%s/ping" % (self.api_endpoint(),"/auth" if auth else "")).text == "\"pong\""
except:
return False
@ -32,11 +35,13 @@ class SeafileClient:
def authorize(self):
self.token = self.obtain_token()
if self.token:
self.session.headers.update({'Authorization': "Token %s" % self.token.token})
return self.token != False
#curl -H 'Authorization: Token 24fd3c026886e3121b2ca630805ed425c272cb96' -H 'Accept: application/json; indent=4' https://cloud.seafile.com/api2/repos/
def libraries(self):
resp=requests.get("%s/repos/" % self.api_endpoint(), headers = {'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4' })
resp=self.session.get("%s/repos/" % self.api_endpoint(), headers = {'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4' })
if not resp.status_code == 200: return
libraries=[]
for lib in resp.json():
@ -48,6 +53,7 @@ class SeafileLibrary:
def __init__(self,client,id,name,owner):
self.client = client
self.session = client.session
self.id = id
self.name = name
self.owner = owner
@ -59,11 +65,11 @@ class SeafileLibrary:
def obtain_link():
#curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/
print("%s/upload-link" % self.api_endpoint())
quoted = requests.get("%s/upload-link" % self.api_endpoint(), headers = {'Authorization': "Token %s" % self.client.token.token,}).text
quoted = self.session.get("%s/upload-link" % self.api_endpoint(), headers = {'Authorization': "Token %s" % self.client.token.token,}).text
return quoted[1:-1]
#curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" -F file=@test.txt -F filename=test.txt -F parent_dir=/ http://cloud.seafile.com:8082/upload-api/73c5d117-3bcf-48a0-aa2a-3f48d5274ae3
resp = requests.post(link or obtain_link() ,
resp = self.session.post(link or obtain_link() ,
files={'file': (file_name,open(file, 'rb')), 'parent_dir': directory, 'target_file': "%s/%s" % (directory,file_name)},
headers = {'Authorization': "Token %s" % self.client.token.token,}
)
@ -73,7 +79,7 @@ class SeafileLibrary:
#curl -H 'Authorization: Token f2210dacd3606d94ff8e61d99b477fd' -H 'Accept: application/json; charset=utf-8; indent=4' https://cloud.seafile.com/api2/repos/dae8cecc-2359-4d33-aa42-01b7846c4b32/file/detail/?p=/foo.c
def file_info(self,path):
resp=requests.get("%s/file/detail/?p=%s" % (self.api_endpoint(),path), headers = {'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4' })
resp=self.session.get("%s/file/detail/?p=%s" % (self.api_endpoint(),path), headers = {'Authorization': "Token %s" % self.token.token, 'Accept': 'application/json; indent=4' })
if resp.status_code == 200:
json = resp.json()
return SeafileFile(self,path,json['id'],json['size'])
@ -88,20 +94,26 @@ class SeafileFile:
self.id = id
self.path = path
self.library = library
self.session = library.session
self.size = size
#curl -v -X PUT -d "p=/foo.md" -H 'Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd' -H 'Accept: application/json; indent=4' https://cloud.seafile.com/api2/repos/afc3b694-7d4c-4b8a-86a4-89c9f3261b12/file/shared-link/
def share(self):
resp = requests.put("%s/repos/%s/file/shared-link/" % (self.library.client.api_endpoint(),self.library.id),
def share(self,expire=None,password=None):
parameters = {'p': self.path }
if expiry:
parameters['expire'] = int(expire)
if password:
parameters['password'] = str(password)
resp = self.session.put("%s/repos/%s/file/shared-link/" % (self.library.client.api_endpoint(),self.library.id),
headers = {'Authorization': "Token %s" % self.library.client.token.token, 'Accept': 'application/json; indent=4'},
data = {'p': self.path}
data = parameters
)
return resp.headers.get("location")
def update(self,file):
def obtain_link():
#curl -H "Authorization: Token f2210dacd9c6ccb8133606d94ff8e61d99b477fd" https://cloud.seafile.com/api2/repos/99b758e6-91ab-4265-b705-925367374cf0/upload-link/
quoted = requests.get("%s/update-link" % self.library.api_endpoint(), headers = {'Authorization': "Token %s" % self.library.client.token.token,}).text
quoted = self.session.get("%s/update-link" % self.library.api_endpoint(), headers = {'Authorization': "Token %s" % self.library.client.token.token,}).text
return quoted[1:-1]
directory, name = os.path.split(self.path)
return self.library.upload(file,name,directory,obtain_link())

27
main.py
View File

@ -22,9 +22,12 @@ class SeafileUploader():
self.uil = QUiLoader()
self.loadSettings()
self.pool = Pool(2)
self.seaf_client = None
self.link_expiry = None
def showSettingsUI(self, parentWidget):
self.parentWidget = parentWidget
#self.processor.configure(parentWidget)
self.settingsDialog = self.uil.load(QFile(workingDir + "/settings.ui"), parentWidget)
self.settingsDialog.group_account.widget_loggedIn.loginButton.connect("clicked()", self.startAuthenticationProcess)
#self.settingsDialog.group_name.input_name.connect("textChanged(QString)", self.nameFormatEdited)
@ -41,13 +44,13 @@ class SeafileUploader():
drop.clear()
drop.setEnabled(False)
select = 0
if self.seaf_client:
if self.seaf_client and self.seaf_client.ping():
self.libs = (self.seaf_client or self.seaf_lib.client).libraries()
i=0
for lib in self.libs:
if self.seaf_lib is not None and lib.id == self.seaf_lib.id:
select = i
print "set %s" % lib.name
print("set %s" % lib.name)
drop.addItem(lib.name)
i=i+1
drop.setCurrentIndex(select)
@ -76,6 +79,7 @@ class SeafileUploader():
(self.lib_id,self.lib_name) = str(settings.value("library", "/")).split("/")
self.lib_path = settings.value("library-path", "")
self.copyLink = settings.value("copy-link", "true") in ['true', True]
self.link_expiry = settings.value("link-expiry", None)
if settings.value("auth-token", False) and settings.value("auth-username", False):
self.access_token = SeafileToken(settings.value("auth-username", False),settings.value("auth-token", False))
else:
@ -86,11 +90,12 @@ class SeafileUploader():
settings.endGroup()
if self.seaf_url and self.access_token:
self.seaf_client = SeafileClient(self.seaf_url,self.access_token.username,token=self.access_token)
for lib in self.seaf_client.libraries():
if lib.id == self.lib_id:
self.seaf_lib = lib
if self.seaf_lib and self.seaf_path:
self.processor = EncryptedProcessor(self.seaf_lib,self.lib_path)#DefaultProcessor(self.seaf_lib,self.lib_path)
if self.seaf_client.ping():
for lib in self.seaf_client.libraries():
if lib.id == self.lib_id:
self.seaf_lib = lib
if self.seaf_lib and self.seaf_path:
self.processor = EncryptedProcessor(self.seaf_lib,self.lib_path)#DefaultProcessor(self.seaf_lib,self.lib_path)
def saveSettings(self):
@ -102,6 +107,8 @@ class SeafileUploader():
except:
pass
settings.setValue("copy-link", self.copyLink)
if self.link_expiry:
settings.setValue("link-expiry", self.link_expiry)
if self.access_token is not None:
settings.setValue("auth-username", self.access_token.username )
settings.setValue("auth-token", self.access_token.token)
@ -112,7 +119,7 @@ class SeafileUploader():
settings.setValue("name-format", self.nameFormat)
settings.setValue("upload-scheme", self.upload_scheme)
#settings.setValue("name-format", self.settingsDialog.group_name.input_name.text)
print self.seaf_lib, self.lib_path
print(self.seaf_lib, self.lib_path)
settings.endGroup()
settings.endGroup()
@ -180,12 +187,12 @@ class SeafileUploader():
selected_lib = drop.currentText
if not drop.isEnabled(): return #not populated
self.lib_path = self.settingsDialog.group_location.widget_location.pathEdit.text
print self.lib_path, selected_lib
print(self.lib_path, selected_lib)
if self.libs is None: return
for lib in self.libs:
if lib.name == selected_lib:
self.seaf_lib = lib
print "%s user selected" % selected_lib
print("%s user selected" % selected_lib)
def login(self,user,password):

View File

@ -1,6 +1,5 @@
rm -rf modules
mkdir modules
pip3 install --install-option="--prefix=$PWD" -r requirements.txt
mv lib/python3.6/site-packages/* modules/
mv lib/python3.5/site-packages/* modules/
cp imports/* -r modules/

View File

@ -1,5 +1,6 @@
asn1crypto==0.24.0
pycrypto
scrypt
#scrypt
bson==0.5.6
salsa20
requests