Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit 5520c95f22fb0fefbdd2d896ba1fa315857b0953 rep committed Sep 24, 2012
Showing with 476 additions and 0 deletions.
  1. +71 −0 README.md
  2. +12 −0 example.txt
  3. +60 −0 gevperf.py
  4. +88 −0 pcat.py
  5. +245 −0 pwrtls.py
  6. BIN pwrtls.pyc
  7. BIN statefile1
  8. BIN statefile2
@@ -0,0 +1,71 @@
+pwrtls - inspired by nacl and curvecp
+=====================================
+
+ - client and server need a long term keypair and generate short-term keypairs for each connection
+ authentication options:
+ 1) server side key is known, clients not authenticated
+ 2) like 1) but server knows a list of client public keys
+ 3) server/client supply each other with signatures on their long-term keys by a trustedkey
+ 4) server and client know a pre-shared symmetric key
+ 5) no known keys, server key displayed to user for verification and storage (compare SSH)
+
+ - we do three round-trips starting with a client hello
+ - optional: the third message (client->server) could include a payload already if needed
+ - messages: client_hello_msg, server_hello_msg, client_verify_msg
+ - after handshake, we use the short-term keys to encrypt and authenticate each message
+ - profit!
+
+==============================
+nacl provides:
+crypto_box(message, nonce, receiver-public-key, sender-private-key)
+ -> asymmetric authenticated encrypted message
+crypto_secretbox(message, nonce, key)
+ -> symmetric authenticated encrypted message
+crypto_sign(message, signkey)
+ -> asymmetric signature on message
+
+------------------------------
+client_hello_msg:
+{
+ spub: short-term public key of client,
+ OPTIONAL pskhint: identifier for resolving psk,
+ OPTIONAL cahint: identifier for trusted pubkey
+}
+
+ -> pskhint only sent when client wants psk auth
+
+------------------------------
+server_hello_msg:
+{
+ box: crypto_box({
+ spub: short-term public key,
+ OPTIONAL pskv: crypto_secretbox(client short-term public key + 1),
+ OPTIONAL cav: crypto_sign(long-term public key)
+ }),
+ lpub: long-term public key
+}
+
+ -> box authenticated with long-term key, encrypted for client short-term key
+ -> nonce used is "2" (short-term key only valid for this connection)
+ -> long-term key is sent in case the client does not yet have it (lookup/display_accept)
+ -> pskv is only sent when client wanted psk auth
+ -> cav is a signature on the servers long-term key by the trusted key
+
+------------------------------
+client_verify_msg:
+{
+ box: crypto_box({
+ lpub: long-term public key,
+ v: crypto_box(short-term public key),
+ vn: verifybox_nonce,
+ OPTIONAL pskv: crypto_secretbox(server short-term public key + 1)
+ OPTIONAL cav: crypto_sign(long-term public key)
+ })
+}
+
+ -> v is verifybox, long-term to long-term key
+ -> box authenticated with short-term key, encrypted for server short-term key
+ -> the public key is sent to be able to look it up on the server's side
+ -> the verifybox vouches for the short-term key used by the client
+ -> pskv and cav see server_hello_msg
+
@@ -0,0 +1,12 @@
+You need:
+ python >= 2.6
+ gevent (python coroutine event loop)
+ bson (from pymongo mongodb driver)
+ nacl (pynacl)
+
+example server cmd:
+ python gevtest.py l --sock 6666 --state pwr.state2 --rpub 89ff96c52137d841270f3eb10818f8f235b62c3e2e396bddec9f6ace3e95f539
+
+example client cmd:
+ python gevtest.py c --sock 127.0.0.1:6666 --state pwr.state --rpub a94986965978a7cdacf276169612ab580f4199f011920607a39469a61e9aa502
+
@@ -0,0 +1,60 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+
+import sys
+import time
+
+import gevent
+import gevent.server
+
+def cooltime(): return time.strftime("%a, %H:%M:%S GMT", time.gmtime())
+
+BUFSIZE = 16384
+DBG = False
+conns = set()
+maxseen = 0
+
+def handle(sock, addr):
+ global DBG, maxseen
+ if DBG: print ' | connection from', addr
+ conns.add(addr)
+ if len(conns) > maxseen:
+ maxseen = len(conns)
+
+ sent = False
+ while True:
+ d = sock.recv(BUFSIZE)
+ if not d: break
+ if DBG: print ' -> RECV', repr(d)
+ if not sent:
+ sock.sendall("HTTP/1.0 200 OK\r\nContent-Length: 7\r\n\r\nS_KNOWN\r\n")
+ sent = True
+ break
+
+ if DBG: print ' \\ connection closed', addr
+ conns.remove(addr)
+ sock.close()
+
+def print_stats():
+ while True:
+ print cooltime(), ' -- {0} active connections, max: {1}'.format(len(conns), maxseen)
+ gevent.sleep(2.0)
+
+def main():
+ print 'gevperf startup!'
+ gevent.spawn(print_stats)
+ server = gevent.server.StreamServer(('0.0.0.0', 61000), handle)
+ server.serve_forever()
+
+ return 0
+
+if __name__ == '__main__':
+ if len(sys.argv) > 1 and sys.argv[1] == 'debug': DBG = True
+ try:
+ sys.exit(main())
+ except KeyboardInterrupt:
+ print >>sys.stderr, 'KeyboardInterrupt: Exiting.'
+ sys.exit(0)
+ except SystemExit:
+ pass
+
@@ -0,0 +1,88 @@
+#!/usr/bin/python
+# -*- coding: utf8 -*-
+
+# pcat, based on pwrtls
+
+# you can test it with a local server and client
+# - pcat listen --sock 6666 --state state1
+# starting the server prints its pub key
+# - pcat connect --sock 127.0.0.1:6666 --state state2 --rpub <remote pub key>
+
+
+import sys
+import traceback
+import argparse
+import logging
+
+import gevent.server
+import gevent.socket
+import gevent.select
+import pwrtls
+
+logger = logging.getLogger('pcat')
+BUFSIZE = 16*1024
+
+def forwardstdin(stdin, sock):
+ try:
+ while True:
+ gevent.select([stdin], [], [])
+ data = stdin.read()
+ if not data:
+ break
+ sock.send(data)
+ except:
+ print 'exc in forwardstdin'
+ traceback.print_exc()
+
+def main():
+ parser = argparse.ArgumentParser(description='pwrcall nacl test.')
+
+ parser.add_argument('action', help='connect/listen', choices=['connect', 'listen', 'c', 'l'])
+ parser.add_argument('--state', dest='state', help='path to state file', default='pwr.state')
+ parser.add_argument('--sock', dest='sock', help='where to connect / what to bind', required=True)
+ parser.add_argument('--rpub', dest='rpub', help='remove public key for verification')
+
+ args = parser.parse_args()
+
+ state = pwrtls.state_file(args.state)
+
+ if args.rpub:
+ args.rpub = args.rpub.decode('hex')
+
+ if args.action[0] == 'c':
+ ip, port = args.sock.split(':', 1)
+ port = int(port)
+
+ socket = gevent.socket.create_connection((ip, port))
+ socket = pwrtls.wrap_socket(socket, **state)
+ #socket.connect()
+ socket.do_handshake()
+ socket.close()
+
+ elif args.action[0] == 'l':
+ if ':' in args.sock: ip, port = args.sock.split(':', 1)
+ else: ip, port = '0.0.0.0', args.sock
+ port = int(port)
+
+ def handle(sock, addr):
+ socket = pwrtls.wrap_socket(sock, server_side=True, **state)
+ socket.do_handshake()
+ socket.close()
+
+ server = gevent.server.StreamServer((ip, port), handle)
+ server.serve_forever()
+
+ return 0
+
+if __name__ == '__main__':
+ logging.basicConfig()
+ try:
+ sys.exit(main())
+ except KeyboardInterrupt:
+ print >>sys.stderr, 'KeyboardInterrupt: Exiting.'
+ sys.exit(0)
+ except SystemExit:
+ pass
+ except:
+ traceback.print_exc()
+ sys.exit(1)
Oops, something went wrong.

0 comments on commit 5520c95

Please sign in to comment.