add cursed TLS to enable crypto.subtle, thx webkit

This commit is contained in:
ed 2019-06-25 21:01:00 +00:00
parent fe83994dc6
commit 9fcd4823b5
8 changed files with 156 additions and 10 deletions

View file

@ -25,5 +25,11 @@ class EnvParams(object):
+ "/copyparty" + "/copyparty"
) )
try:
os.mkdir(self.cfg)
except:
if not os.path.isdir(self.cfg):
raise
E = EnvParams() E = EnvParams()

View file

@ -8,10 +8,14 @@ __copyright__ = 2019
__license__ = "MIT" __license__ = "MIT"
__url__ = "https://github.com/9001/copyparty/" __url__ = "https://github.com/9001/copyparty/"
import os
import shutil
import filecmp
import locale import locale
import argparse import argparse
from textwrap import dedent from textwrap import dedent
from .__init__ import E
from .__version__ import S_VERSION, S_BUILD_DT from .__version__ import S_VERSION, S_BUILD_DT
from .svchub import SvcHub from .svchub import SvcHub
@ -35,7 +39,7 @@ class RiceFormatter(argparse.HelpFormatter):
return "".join(indent + line + "\n" for line in text.splitlines()) return "".join(indent + line + "\n" for line in text.splitlines())
def main(): def ensure_locale():
for x in [ for x in [
"en_US.UTF-8", "en_US.UTF-8",
"English_United States.UTF8", "English_United States.UTF8",
@ -48,6 +52,37 @@ def main():
except: except:
continue continue
def ensure_cert():
"""
the default cert (and the entire TLS support) is only here to enable the
crypto.subtle javascript API, which is necessary due to the webkit guys
being massive memers (https://www.chromium.org/blink/webcrypto)
i feel awful about this and so should they
"""
cert_insec = os.path.join(E.mod, "res/insecure.pem")
cert_cfg = os.path.join(E.cfg, "cert.pem")
if not os.path.exists(cert_cfg):
shutil.copy2(cert_insec, cert_cfg)
try:
if filecmp.cmp(cert_cfg, cert_insec):
print(
"\033[33m\n using default TLS certificate; https will be insecure."
+ "\033[36m\n certificate location: {}\033[0m\n".format(cert_cfg)
)
except:
pass
# speaking of the default cert,
# printf 'NO\n.\n.\n.\n.\ncopyparty-insecure\n.\n' | faketime '2000-01-01 00:00:00' openssl req -x509 -sha256 -newkey rsa:2048 -keyout insecure.pem -out insecure.pem -days $((($(printf %d 0x7fffffff)-$(date +%s --date=2000-01-01T00:00:00Z))/(60*60*24))) -nodes && ls -al insecure.pem && openssl x509 -in insecure.pem -text -noout
def main():
ensure_locale()
ensure_cert()
ap = argparse.ArgumentParser( ap = argparse.ArgumentParser(
formatter_class=RiceFormatter, formatter_class=RiceFormatter,
prog="copyparty", prog="copyparty",

View file

@ -3,6 +3,8 @@
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import os import os
import ssl
import socket
import jinja2 import jinja2
from .__init__ import E from .__init__ import E
@ -16,15 +18,14 @@ class HttpConn(object):
creates an HttpCli for each request (Connection: Keep-Alive) creates an HttpCli for each request (Connection: Keep-Alive)
""" """
def __init__(self, sck, addr, args, auth, log_func): def __init__(self, sck, addr, args, auth, log_func, cert_path):
self.s = sck self.s = sck
self.addr = addr self.addr = addr
self.args = args self.args = args
self.auth = auth self.auth = auth
self.cert_path = cert_path
self.sr = Unrecv(sck)
self.workload = 0 self.workload = 0
self.log_func = log_func self.log_func = log_func
self.log_src = "{} \033[36m{}".format(addr[0], addr[1]).ljust(26) self.log_src = "{} \033[36m{}".format(addr[0], addr[1]).ljust(26)
@ -37,9 +38,38 @@ class HttpConn(object):
def respath(self, res_name): def respath(self, res_name):
return os.path.join(E.mod, "web", res_name) return os.path.join(E.mod, "web", res_name)
def log(self, msg):
self.log_func(self.log_src, msg)
def run(self): def run(self):
method = None
if self.cert_path:
method = self.s.recv(4, socket.MSG_PEEK)
if len(method) != 4:
err = b"need at least 4 bytes in the first packet; got {}".format(
len(method)
)
self.log(err)
self.s.send(b"HTTP/1.1 400 Bad Request\r\n\r\n" + err)
return
if method not in [None, b"GET ", b"HEAD", b"POST"]:
self.log_src = self.log_src.replace("[36m", "[35m")
try:
self.s = ssl.wrap_socket(
self.s, server_side=True, certfile=self.cert_path
)
except OSError as ex:
if "ALERT_BAD_CERTIFICATE" in ex.strerror:
self.log("client rejected our certificate (nice)")
else:
raise
return
self.sr = Unrecv(self.s)
while True: while True:
cli = HttpCli(self) cli = HttpCli(self)
if not cli.run(): if not cli.run():
self.s.close()
return return

View file

@ -2,9 +2,11 @@
# coding: utf-8 # coding: utf-8
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import os
import time import time
import threading import threading
from .__init__ import E
from .httpconn import HttpConn from .httpconn import HttpConn
from .authsrv import AuthSrv from .authsrv import AuthSrv
@ -27,6 +29,12 @@ class HttpSrv(object):
self.workload_thr_alive = False self.workload_thr_alive = False
self.auth = AuthSrv(args, log_func) self.auth = AuthSrv(args, log_func)
cert_path = os.path.join(E.cfg, "cert.pem")
if os.path.exists(cert_path):
self.cert_path = cert_path
else:
self.cert_path = None
def accept(self, sck, addr): def accept(self, sck, addr):
"""takes an incoming tcp connection and creates a thread to handle it""" """takes an incoming tcp connection and creates a thread to handle it"""
thr = threading.Thread(target=self.thr_client, args=(sck, addr, self.log)) thr = threading.Thread(target=self.thr_client, args=(sck, addr, self.log))
@ -42,7 +50,7 @@ class HttpSrv(object):
def thr_client(self, sck, addr, log): def thr_client(self, sck, addr, log):
"""thread managing one tcp client""" """thread managing one tcp client"""
cli = HttpConn(sck, addr, self.args, self.auth, log) cli = HttpConn(sck, addr, self.args, self.auth, log, self.cert_path)
with self.mutex: with self.mutex:
self.clients[cli] = 0 self.clients[cli] = 0
self.workload += 50 self.workload += 50
@ -57,6 +65,7 @@ class HttpSrv(object):
cli.run() cli.run()
finally: finally:
sck.close()
with self.mutex: with self.mutex:
del self.clients[cli] del self.clients[cli]

View file

@ -0,0 +1,48 @@
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQDD84b31Brvmfcc
GTwXuf5n5NW6KxIhLWVZgsCJq+lwGTGl1Cqm9vnzZqD7MDLNzztWk51FDXxeJy9Z
Vos1F+n3qfDi4zdd8zWIJxLTsIEkqZjSWiQqqmFijXoaEPBsQ32qRZprh/FfNw3P
TagroIgpgzd196eoWdDdggenYBG8/PbktaBKgOo6jz6Q5/UzNTmvcbtlxlsxPos8
aTqzezCgA/cArg8mxmfvRPrw8umC8ATiVLvGrXeMiSvcr8Els5air/idhYC8u5zO
zGZCePxoKUptQvh/06jsye1S3JOf+RsfAWvu7DgWvSJU2dHfVmtb4ZMSa6cjqTPP
cW7S1WyLAgMBAAECggEAAwabqvAHinOiMTjiiKtClnAeLMXFfeWpjvxJ5NZWwHhj
H+Bq2DEwIuYOzlIsNqlgjTGyWAKhTQLl5EdF1wgLgNuK8LX5gOXkibmwvLwZAmvs
BDOII3CGGHN+0zA3xjQ0mJCCle5/d6zt9amJU0MjVyDDlnrAiAT7CLCdVaRSIczv
SETM90NBVHNMraB+Tz9kR1FhyDDlzqpRn/19Hm4BG6/iSmPYpL3jgCRWNweXSXdd
x9EitJCz6Yo9+0qPD8DayrH5sKviZw0QDXUezWCcQvgXMsv5MJa7+UaawLk6bq5B
Ou1TFGuFdNeNmWofAwPE8UOSIXoKhBK84z5lpTs44QKBgQDg9bRrn3a2u8vpx3r7
B+v4dlVq5I3lbVwLwEo6vnw2UiIOtQaQMiWX8HoHR3rmzCVMZq8xNNhEYSboFgtB
bbQaxoApdqVTF700c5Le82tj3rBXN0vBIUm6Nzpz0IKQw8HWGz1q/sHd8uZdmtAO
m4JrZIJqMItcLhyjYCp8hRUkvwKBgQDe/SZVCBMocHASyB4RgeAkNKSjFEYe2yAl
23W9Me3koJqTvHEZnv3Xk5O3HF0dxElktJW5slyrA2BcrzuusO+pge/29OV4ypFa
kI51ebMgO3hm2BKKMwxR3mT4KVAbKveLi7hdlnbHVCcZelygkw+11tzIfaikeswR
c4FoyPpvNQKBgDEA1OBsyCteFTlDnuJ4A0sIW+sBBnfnrplQtdq+C8i5c3nIrTlT
8yR52dskEv2bkrRl2dvaKxIaJ6N+yczi3MzIWLqvgavsC+cVFfVDCS2kIL2e6f2U
Br9tsGnyDb8DJYJCRMq92/VBKDVTt+a2sV47cr02/eSClvJvzFF7m/N5AoGAJYzD
k7YUY87rUH5aceBI+k/TGZMka7XCqB1Yqk9qHAHfhdlJwmK/pDm5ujAQjh6rrUWr
oOWkLTgYVgM8LaKl+Qlke1Wp/rk92N5W3vlrbJYXJFpmZNdLz81/ezqZvrlxjhIt
LbVUsyQ8oVG1n2SkVJ6l9y0R5QC4tIea1yZg5bECgYEAhF2EOtBCJ3EDEpHvnlK7
OuLRVjce4Vy+Mj3nFbHQMJf+A1B7+B43Ce1fn94OfGJldLDjvaZb8O91JAkHAwJQ
GCpOVj9go0ffg/2hurtXW3bMLXIXqcn1538MB5NdlcL22+T0CCprAeuRQm4saMhz
mkt5VszEuF/c88usk0ZMm4Y=
-----END PRIVATE KEY-----
-----BEGIN CERTIFICATE-----
MIIDNTCCAh2gAwIBAgIUWWDVQNmrPPo2HlyhYuSYY2uKPbEwDQYJKoZIhvcNAQEL
BQAwKjELMAkGA1UEBhMCTk8xGzAZBgNVBAMMEmNvcHlwYXJ0eS1pbnNlY3VyZTAe
Fw0wMDAxMDEwMDAwMDBaFw0zODAxMTkwMDAwMDBaMCoxCzAJBgNVBAYTAk5PMRsw
GQYDVQQDDBJjb3B5cGFydHktaW5zZWN1cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IB
DwAwggEKAoIBAQDD84b31BrvmfccGTwXuf5n5NW6KxIhLWVZgsCJq+lwGTGl1Cqm
9vnzZqD7MDLNzztWk51FDXxeJy9ZVos1F+n3qfDi4zdd8zWIJxLTsIEkqZjSWiQq
qmFijXoaEPBsQ32qRZprh/FfNw3PTagroIgpgzd196eoWdDdggenYBG8/PbktaBK
gOo6jz6Q5/UzNTmvcbtlxlsxPos8aTqzezCgA/cArg8mxmfvRPrw8umC8ATiVLvG
rXeMiSvcr8Els5air/idhYC8u5zOzGZCePxoKUptQvh/06jsye1S3JOf+RsfAWvu
7DgWvSJU2dHfVmtb4ZMSa6cjqTPPcW7S1WyLAgMBAAGjUzBRMB0GA1UdDgQWBBQC
uhBjOit55cEz7jMIiJq2BljqxDAfBgNVHSMEGDAWgBQCuhBjOit55cEz7jMIiJq2
BljqxDAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCu7VE/Pt4H
IOHAacQFJN9rdUtI1lcvbJEMdpPglHgFa546pgzDjS8QGvULmppb4mkMbezZ2u+v
ZsWMfLxATYgfqEPO/fnT/UqFX2TI2pmmZhqSV+eW8AGRPD5LYyfXpvU4ciSz4Fyl
FlrcCOStbeOjHwpeF+n92sF6/LL3n4yy1jmLnTaZDdd59PcWpQkX8omBWlXPdCPR
ot0ZI2OpK5mDUG2FglCXl29k1q/zLsKPyfmG7T6VOMB8HO7EylzgSTke4WH9QLR/
bkn7AZU+JEIdC6SgR0WSQAntqM+b/OuYSBzXDX0XLwnyY2Dx8SshKNF9v6GwTq7j
yQev95xStUTw
-----END CERTIFICATE-----

View file

@ -6,9 +6,9 @@
<title>copyparty</title> <title>copyparty</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=0.8"> <meta name="viewport" content="width=device-width, initial-scale=0.8">
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/browser.css"> <link rel="stylesheet" type="text/css" media="screen" href="/.cpr/browser.css{{ ts }}">
{%- if can_upload %} {%- if can_upload %}
<link rel="stylesheet" type="text/css" media="screen" href="/.cpr/upload.css"> <link rel="stylesheet" type="text/css" media="screen" href="/.cpr/upload.css{{ ts }}">
{%- endif %} {%- endif %}
</head> </head>

View file

@ -46,7 +46,7 @@
font-size: 2em; font-size: 2em;
margin: 1em auto; margin: 1em auto;
padding: 1em 0; padding: 1em 0;
width: 15em; width: 12em;
cursor: pointer; cursor: pointer;
box-shadow: .4em .4em 0 #111; box-shadow: .4em .4em 0 #111;
} }
@ -71,7 +71,7 @@
} }
#u2conf { #u2conf {
margin: 1em auto; margin: 1em auto;
width: 30em; width: 26em;
} }
#u2conf * { #u2conf * {
text-align: center; text-align: center;

View file

@ -39,6 +39,10 @@ combines parts into final file if all-ok
use sha512 instead of sha256 everywhere use sha512 instead of sha256 everywhere
write directly to .$wark.tmp instead of parts, then move to destination write directly to .$wark.tmp instead of parts, then move to destination
may have to forego the predictable wark and instead use random key:
webkit is doing the https-only meme for crypto.subtle.*
so native sha512 is unavailable on LAN (oh no)
so having the client hash everything before first byte is NG
@ -52,6 +56,20 @@ registry moves file into place when all chunks are verified
##
## in case we can't rely on sha512 of entire file
handshake
client gets wark (random session-key)
localstorage[filename+size] = wark
thread 1: sha512 chunks
thread 2: upload chunks
server renames wark to filename on last chunk finished
if conflict with another wark during upload: all files are renamed
if conflict with existing filename: new file is renamed
## ##
## required capabilities ## required capabilities