def upload_file(cls, user, data): from hashlib import sha512 from config import allowed_file_suffix_list filename = data.filename assert len(filename) <= 64, 'filename too long (>64B)' assert filename_pattern.fullmatch(filename), 'no unicode character allowed' filename_suffix = filename.rsplit('.', maxsplit=1)[-1] assert filename_suffix in allowed_file_suffix_list, 'banned file type' f = File.query.filter(and_(File.creator_id == user.id_, File.filename == filename)).first() assert not f, 'file already exists' content = data.read() assert len(content) < 1*1024*1024, 'file too large (>=10MB)' user_id = str(user.id_)+'/' if not path.exists(storage_path+user_id): if not path.exists(storage_path): mkdir(storage_path) mkdir(storage_path+user_id) # 计算原文件的哈希 hash_value = sha512(content).hexdigest() # 判断文件是否存在 if not path.exists(storage_path+user_id+hash_value): # 加密并存储。加密前得先还原出对称密钥。 content = secret.symmetric_encrypt(secret.decrypt(user.encrypted_symmetric_key), content) # 同时计算签名 signature = secret.sign(content) # 保存密文与签名 with open(storage_path+user_id+hash_value, 'wb') as f: f.write(content) with open(storage_path+user_id+hash_value+'.sig', 'wb') as f: f.write(signature) creator_id = user.id_ file = File(creator_id=creator_id, filename=filename, hash_value=hash_value) db.session.add(file) db.session.commit()
def show_msg_detail(): from config import domain_name, sy_private_key from flask import jsonify from models import Share import secret, traceback try: shareid = request.form['sid'] msgid = request.form['mid'] share = Share.get_by(id_=shareid) Message.set_readed(msgid) link = 'http://' + domain_name + '/share/verify?fid=' + str( share.fid) + '&nonce=' + share.nonce # 使用服务器私钥解密分享码 sk = secret.decrypt(bytes.fromhex(sy_private_key), share.enc_sharekey) return jsonify(link=link, sharekey=sk.hex()) except Exception as e: return e.args
def download_file(cls, user, fid, type_): from flask import make_response, send_file from collections import OrderedDict import unicodedata from werkzeug.http import dump_options_header from werkzeug.urls import url_quote f = File.query.filter(File.fileid == fid).first() assert f, '文件不存在' hash_value = f.sha256 filename = f.filename user_ = OnlineUser.query.filter(OnlineUser.usrid == user.usrid).first() # 获取私钥解密得对称密钥 Pk = user_.Pk enc_key = secret.decrypt(Pk, user.symkey) with open(storage_path + str(user.usrid) + '/' + hash_value, 'rb') as f_: content = f_.read() if type_ == 'encrypted': filename = filename + '.encrypted' elif type_ == 'hashvalue': content = hash_value filename = filename + '.hash' else: content = secret.symmetric_decrypt(enc_key, content) if type_ == 'signature': content = secret.sign(Pk, content) filename = filename + '.sig' response = make_response(content) filenames = OrderedDict() try: filename = filename.encode('latin-1') except UnicodeEncodeError: filenames['filename'] = unicodedata.normalize('NFKD', filename).encode('latin-1', 'ignore') filenames['filename*']: "UTF-8''{}".format(url_quote(filename)) else: filenames['filename'] = filename response.headers.set('Content-Disposition', 'attachment', **filenames) return response
def decrypt_and_encrypt(cls, fid, user, sym_key): from .file import File from .online_user import OnlineUser import secret # 获取私钥解密获得对称密钥 usr = OnlineUser.query.filter(OnlineUser.usrid == user.usrid).first() Pk = usr.Pk enc_key = secret.decrypt(Pk, user.symkey) # 查询、读取本地存储的加密文件 f = File.get_by(fileid = fid) hash_value = f.sha256 with open(storage_path + str(user.usrid) + '/' + hash_value, 'rb') as f: content = f.read() # 解密文件 content = secret.symmetric_decrypt(enc_key, content) # 用私钥签名 sig = secret.sign(Pk, content) # 用新的会话密钥加密文件 new_content = secret.symmetric_encrypt(sym_key, content) # 写入 shared_path user_id = str(user.usrid) + '/' head = shared_path + user_id full_path = head + hash_value if not path.exists(head): if not path.exists(shared_path): mkdir(shared_path) mkdir(head) # 判断文件是否存在 # 分享文件夹要写入签名 if not path.exists(full_path): with open(full_path, 'wb') as f: f.write(new_content) with open(full_path + '.sig', 'wb') as f: f.write(sig)
def upload_file(cls, user, data): from hashlib import sha256 from config import allowed_file_list filename = data.filename assert len(filename) <= 64, '文件名过长(>64B)' assert filename_pattern.fullmatch(filename), '文件名中不能包含以下字符:\n\\ / : * ? " < > |' mime_type = data.mimetype assert mime_type in allowed_file_list, '不支持的文件类型' content = data.read() hash_value = sha256(content).hexdigest() f = File.query.filter(and_(File.uid == user.usrid, File.sha256 == hash_value)).first() assert not f, '文件已存在' size = convert_bytes(len(content)) assert len(content) < 20*1024*1024, '文件过大(>=20MB)' userid = str(user.usrid) + '/' hash_value = sha256(content).hexdigest() head = storage_path + userid if not path.exists(head): if not path.exists(storage_path): mkdir(storage_path) mkdir(head) if not path.exists(head + hash_value): # 还原对称密钥 Pkv = OnlineUser.get_by(usrid = userid).Pk encrypted_symkey = User.get_by(usrid = userid).symkey symkey = secret.decrypt(Pkv, encrypted_symkey) # 加密 content = secret.symmetric_encrypt(symkey, content) # 只保存密文 with open(head + hash_value, 'wb') as f: f.write(content) fi = File(uid=user.usrid, filename=filename, size=size, sha256=hash_value) db.session.add(fi) db.session.commit()
def download_file(cls, user, filename, type_): from flask import make_response f = File.query.filter(and_(File.creator_id == user.id_, File.filename == filename)).first() assert f, 'no such file ({})'.format(filename) hash_value = f.hash_value if type_ == 'hashvalue': content = hash_value filename = filename + '.hash' elif type_ == 'signature': # 读取签名 with open(storage_path+str(user.id_)+'/'+hash_value+'.sig', 'rb') as f_: content = f_.read() filename = filename+'.sig' else: # 读取密文 with open(storage_path+str(user.id_)+'/'+hash_value, 'rb') as f_: content = f_.read() if type_ == 'plaintext': content = secret.symmetric_decrypt(secret.decrypt(user.encrypted_symmetric_key), content) elif type_ == 'encrypted': filename = filename + '.encrypted' response = make_response(content) response.headers['Content-Disposition'] = 'attachment; filename={}'.format(filename) return response