#!/usr/bin/env python # # Copyright (c) 2016-2017, NORDUnet A/S All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are # met: # - Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # - Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # - Neither the name of the NORDUnet nor the names of its contributors may # be used to endorse or promote products derived from this software # without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS # IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A # PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT # HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. """ Implementation of Cryptech RPC protocol multiplexer in Python. Unlike the original C implementation, this uses SLIP encapsulation over a SOCK_STREAM channel, because support for SOCK_SEQPACKET is not what we might wish. We outsource all the heavy lifting for serial and network I/O to the PySerial and Tornado libraries, respectively. """ import os import sys import time import struct import atexit import weakref import logging import argparse import logging.handlers import serial import serial.tools.list_ports_posix import tornado.tcpserver import tornado.iostream import tornado.netutil import tornado.ioloop import tornado.queues import tornado.locks import tornado.gen from cryptech.libhal import HAL_OK, RPC_FUNC_GET_VERSION, RPC_FUNC_LOGOUT, RPC_FUNC_LOGOUT_ALL logger = logging.getLogger("cryptech_muxd") SLIP_END = chr(0300) # Indicates end of SLIP packet SLIP_ESC = chr(0333) # Indicates byte stuffing SLIP_ESC_END = chr(0334) # ESC ESC_END means END data byte SLIP_ESC_ESC = chr(0335) # ESC ESC_ESC means ESC data byte Control_U = chr(0025) # Console: clear line Control_M = chr(0015) # Console: end of line def slip_encode(buffer): "Encode a buffer using SLIP encapsulation." return SLIP_END + buffer.replace(SLIP_ESC, SLIP_ESC + SLIP_ESC_ESC).replace(SLIP_END, SLIP_ESC + SLIP_ESC_END) + SLIP_END def slip_decode(buffer): "Decode a SLIP-encapsulated buffer." return buffer.strip(SLIP_END).replace(SLIP_ESC + SLIP_ESC_END, SLIP_END).replace(SLIP_ESC + SLIP_ESC_ESC, SLIP_ESC) def client_handle_get(msg): "Extract client_handle field from a Cryptech RPC message." return struct.unpack(">L", msg[4:8])[0] def client_handle_set(msg, handle): "Replace client_handle field in a Cryptech RPC message." return msg[:4] + struct.pack(">L", handle) + msg[8:] logout_msg = struct.pack(">LL", RPC_FUNC_LOGOUT, 0) logout_all_msg = struct.pack(">LL", RPC_FUNC_LOGOUT_ALL, 0) class SerialIOStream(tornado.iostream.BaseIOStream): """ Implementation of a Tornado IOStream over a PySerial device. """ # In theory, we want zero (non-blocking mode) for both the read # and write timeouts here so that PySerial will let Tornado handle # all the select()/poll()/epoll()/kqueue() fun, delivering maximum # throughput to all. In practice, this has always worked for the # author, but another developer reports that on some (not all) # platforms this fails consistently with Tornado reporting write # timeout errors, presumably as the result of receiving an IOError # or OSError exception from PySerial. For reasons we don't really # understand, setting a PySerial write timeout on the order of # 50-100 ms "solves" this problem. Again in theory, this will # result in lower throughput if PySerial spends too much time # blocking on a single serial device when Tornado could be doing # something useful elsewhere, but such is life. def __init__(self, device): self.serial = serial.Serial(device, 921600, timeout = 0, write_timeout = 0.1) self.serial_device = device super(SerialIOStream, self).__init__() def fileno(self): return self.serial.fileno() def close_fd(self): self.serial.close() def write_to_fd(self, data): return self.serial.write(data) def read_from_fd(self): return self.serial.read(self.read_chunk_size) or None class PFUnixServer(tornado.tcpserver.TCPServer): """ Variant on tornado.tcpserver.TCPServer, listening on a PF_UNIX (aka PF_LOCAL) socket instead of a TCP socket. """ def __init__(self, serial_stream, socket_filename, mode = 0600): super(PFUnixServer, self).__init__() self.serial = serial_stream self.socket_filename = socket_filename self.add_socket(tornado.netutil.bind_unix_socket(socket_filename, mode)) atexit.register(self.atexit_unlink) def atexit_unlink(self): try: os.unlink(self.socket_filename) except: pass class RPCIOStream(SerialIOStream): """ Tornado IOStream for a serial RPC channel. """ def __init__(self, device): super(RPCIOStream, self).__init__(device) self.queues = weakref.WeakValueDictionary() self.rpc_input_lock = tornado.locks.Lock() @tornado.gen.coroutine def rpc_input(self, query, handle = 0, queue = None): "Send a query to the
[[PageOutline]]

= !CrypTech Workshop - 2015.07.18 Praha =

== Logistics ==
* Hilton Hotel, the IETF venue
* Amsterdam Room (this is a change)
* 09:00 - 17:00

== Introductions ==
* The !CrypTech Team
* Others Who Have Contributed
* Other Folk at the Meeting

== Workshop Goals ==
* Get an understanding of the project status and roadmap
* Discus your requirements and expectations with the team
* Get hands-on experience with the cryptech code on the novena dev board
* Procrastinate finding a new name for the project
* NON-GOAL: you don't get to go home with hardware

== Overview of Project ==
* Overall Goals
 * A Set of Designs, not a product
 * Save the World, Securely :)
* Short Term Goals
 * Sign a DNSSEC Zone
 * Sign RPKI Data
* Roadmap
 * Novena Dev Platform
 * Noise Board
 * Bridge Board
 * Alpha Board
 * !Better/Beta Board :)

== Status ==
* Novena, Bridge, and Alpha Hardware
* Verilog Cores
* Software
* APIs
* Current Build and Work Packaging

== User Feedback So Far ==

== Workshop 0 ==
* Unpack Novenas
* Install Noise Board
* Build Bitstream and Download
* Build Software and Install
* Install DNSSEC Signware on Your Laptop
* Sign a Zone

== User Feedback So Far ==

== Workshop 1 ==
* Unpack and Install Bridge Boards
* Build Software and Install
* Install DNSSEC Signware on Your Laptop
* Sign a Zone

== User Feedback ==

== Morning coffee break :) ==

== You Should Bring ==
* Laptop
* MacOSX, Linux, or ?  (if you want to build FPGAware, debian 64-bit, and it needs a desktop, VM OK)
* Micro-SD card if you want to take images and/or data home
* Logic Analyzer (optional, for hardware geeks)
* JTAG Interface (optional, for hardware geeks)
* Cookies or Equivalent to Share
False assert not is_cty or not is_rpc result = None if is_cty: result = "cty" yield self.write(Control_U) if is_rpc: result = "rpc" yield self.write(SLIP_END) self.close() raise tornado.gen.Return(result) @tornado.gen.coroutine def main(): parser = argparse.ArgumentParser(formatter_class = argparse.ArgumentDefaultsHelpFormatter) parser.add_argument("-v", "--verbose", action = "count", help = "blather about what we're doing") parser.add_argument("-l", "--log-file", help = "log to file instead of stderr") parser.add_argument("-L", "--console-log", type = argparse.FileType("a"), help = "log console output to file") parser.add_argument("-p", "--probe", nargs = "*", metavar = "DEVICE", help = "probe for device UARTs") parser.add_argument("--rpc-device", help = "RPC serial device name", default = os.getenv("CRYPTECH_RPC_CLIENT_SERIAL_DEVICE")) parser.add_argument("--rpc-socket", help = "RPC PF_UNIX socket name", default = os.getenv("CRYPTECH_RPC_CLIENT_SOCKET_NAME", "/tmp/.cryptech_muxd.rpc")) parser.add_argument("--cty-device", help = "CTY serial device name", default = os.getenv("CRYPTECH_CTY_CLIENT_SERIAL_DEVICE")) parser.add_argument("--cty-socket", help = "CTY PF_UNIX socket name", default = os.getenv("CRYPTECH_CTY_CLIENT_SOCKET_NAME", "/tmp/.cryptech_muxd.cty")) args = parser.parse_args() if args.log_file is not None: logging.getLogger().handlers[:] = [logging.handlers.WatchedFileHandler(args.log_file)] logging.getLogger().handlers[0].setFormatter( logging.Formatter("%(asctime)-15s %(name)s[%(process)d]:%(levelname)s: %(message)s", "%Y-%m-%d %H:%M:%S")) if args.verbose: logging.getLogger().setLevel(logging.DEBUG if args.verbose > 1 else logging.INFO) if args.probe is not None: yield ProbeIOStream.run_probes(args) if args.console_log is not None: console_log = tornado.iostream.PipeIOStream(args.console_log.fileno()) else: console_log = None futures = [] if args.rpc_device is None: logger.warn("No RPC device found") else: rpc_stream = RPCIOStream(device = args.rpc_device) rpc_server = RPCServer(rpc_stream, args.rpc_socket) futures.append(rpc_stream.rpc_output_loop()) futures.append(rpc_stream.logout_all()) if args.cty_device is None: logger.warn("No CTY device found") else: cty_stream = CTYIOStream(device = args.cty_device, console_log = console_log) cty_server = CTYServer(cty_stream, args.cty_socket) futures.append(cty_stream.cty_output_loop()) # Might want to use WaitIterator(dict(...)) here so we can # diagnose and restart output loops if they fail? if futures: yield futures if __name__ == "__main__": try: tornado.ioloop.IOLoop.current().run_sync(main) except (SystemExit, KeyboardInterrupt): pass except: logger.exception("Unhandled exception") else: logger.debug("Main loop exited")