Edit

IABSD.fr/src/sys/dev/usb/uonerng.c

Branch :

  • Show log

    Commit

  • Author : deraadt
    Date : 2020-05-29 04:42:23
    Hash : c8f27247
    Message : dev/rndvar.h no longer has statistical interfaces (removed during various conversion steps). it only contains kernel prototypes for 4 interfaces, all of which legitimately belong in sys/systm.h, which are already included by all enqueue_randomness() users.

  • sys/dev/usb/uonerng.c
  • /*	$OpenBSD: uonerng.c,v 1.5 2020/05/29 04:42:25 deraadt Exp $ */
    /*
     * Copyright (C) 2015 Devin Reade <gdr@gno.org>
     * Copyright (C) 2015 Sean Levy <attila@stalphonsos.com>
     * Copyright (c) 2007 Marc Balmer <mbalmer@openbsd.org>
     * Copyright (c) 2006 Alexander Yurchenko <grange@openbsd.org>
     * Copyright (c) 1998 The NetBSD Foundation, Inc.
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    /*
     * Moonbase Otago OneRNG TRNG.  Note that the encoded vendor for this
     * device is OpenMoko as OpenMoko has made its device ranges available
     * for other open source / open hardware vendors.
     *
     * Product information can be found here:
     *     http://onerng.info/onerng
     *
     * Based on the ualea(4), uow(4), and umodem(4) source code.
     */
    
    #include <sys/param.h>
    #include <sys/systm.h>
    #include <sys/device.h>
    #include <sys/time.h>
    #include <sys/timeout.h>
    #include <machine/bus.h>
    
    #include <dev/usb/usb.h>
    #include <dev/usb/usbdi.h>
    #include <dev/usb/usbdivar.h>
    #include <dev/usb/usbdi_util.h>
    #include <dev/usb/usbdevs.h>
    #include <dev/usb/usbcdc.h>
    
    /*
     * The OneRNG is documented to provide ~350kbits/s of entropy at
     * ~7.8 bits/byte, and when used at a lower rate providing close
     * to 8 bits/byte.
     *
     * Although this driver is able to consume the data at the full rate,
     * we tune this down to 10kbit/s as the OpenBSD RNG is better off
     * with small amounts of input at a time so as to not saturate the
     * input queue and mute other sources of entropy.
     *
     * Furthermore, unlike other implementations, for us there is no benefit
     * to discarding the initial bytes retrieved from the OneRNG, regardless
     * of the quality of the data. (Empirical tests suggest that the initial
     * quality is fine, anyway.)
     */
    #define ONERNG_BUFSIZ		128
    #define ONERNG_MSECS		100
    
    #define ONERNG_TIMEOUT  	1000	/* ms */
    
    /*
     * Define ONERNG_MEASURE_RATE to periodically log rate at which we provide
     * random data to the kernel.
     */
    #ifdef ONERNG_MEASURE_RATE
    #define ONERNG_RATE_SECONDS 30
    #endif
    
    /* OneRNG operational modes */
    #define ONERNG_OP_ENABLE	"cmdO\n" /* start emitting data */
    #define ONERNG_OP_DISABLE	"cmdo\n" /* stop emitting data */
    #define ONERNG_OP_FLUSH_ENTROPY	"cmdw\n"
    
    /* permits extracting the firmware in order to check the crypto signature */
    #define ONERNG_OP_EXTRACT_FIRMWARE "cmdX\n"
    
    /*
     * Noise sources include an avalache circuit and an RF circuit.
     * There is also a whitener to provide a uniform distribution.
     * Different combinations are possible.
     */
    #define ONERNG_AVALANCHE_WHITENER	"cmd0\n" /* device default */
    #define ONERNG_AVALANCHE		"cmd1\n"
    #define ONERNG_AVALANCHE_RF_WHITENER	"cmd2\n"
    #define ONERNG_AVALANCHE_RF		"cmd3\n"
    #define ONERNG_SILENT			"cmd4\n" /* none; necessary for cmdX */
    #define ONERNG_SILENT2			"cmd5\n"
    #define ONERNG_RF_WHITENER		"cmd6\n"
    #define ONERNG_RF			"cmd7\n"
    
    
    #define ONERNG_IFACE_CTRL_INDEX	0
    #define ONERNG_IFACE_DATA_INDEX	1
    
    #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
    
    struct uonerng_softc {
    	struct	  device sc_dev;
    	struct	  usbd_device *sc_udev;
    
    	int	  sc_ctl_iface_no;			/* control */
    	struct	  usbd_interface *sc_data_iface;	/* data */
    
    	struct	  usbd_pipe *sc_inpipe;
    	struct	  usbd_pipe *sc_outpipe;
    
    	struct	  timeout sc_timeout;
    	struct	  usb_task sc_task;
    	struct	  usbd_xfer *sc_xfer;
    	int      *sc_buf;
    #ifdef ONERNG_MEASURE_RATE
    	struct	  timeval sc_start;
    	struct	  timeval sc_cur;
    	int	  sc_counted_bytes;
    #endif
    	u_char	  sc_dtr;			/* current DTR state */
    	u_char	  sc_rts;			/* current RTS state */
    	u_char	  sc_first_run;
    };
    
    int uonerng_match(struct device *, void *, void *);
    void uonerng_attach(struct device *, struct device *, void *);
    int uonerng_detach(struct device *, int);
    void uonerng_task(void *);
    void uonerng_timeout(void *);
    int uonerng_enable(struct uonerng_softc *sc);
    void uonerng_cleanup(struct uonerng_softc *sc);
    usbd_status uonerng_set_line_state(struct uonerng_softc *sc);
    usbd_status uonerng_rts(struct uonerng_softc *sc, int onoff);
    
    struct cfdriver uonerng_cd = {
    	NULL, "uonerng", DV_DULL
    };
    
    const struct cfattach uonerng_ca = {
    	sizeof(struct uonerng_softc), uonerng_match, uonerng_attach, uonerng_detach
    };
    
    int
    uonerng_match(struct device *parent, void *match, void *aux)
    {
    	struct usb_attach_arg *uaa = aux;
    
    	if (uaa->iface == NULL)
    		return UMATCH_NONE;
    
    	if (uaa->vendor != USB_VENDOR_OPENMOKO2 ||
    	    uaa->product != USB_PRODUCT_OPENMOKO2_ONERNG)
    		return UMATCH_NONE;
    
    	return UMATCH_VENDOR_PRODUCT;
    }
    
    void
    uonerng_attach(struct device *parent, struct device *self, void *aux)
    {
    	struct uonerng_softc *sc = (struct uonerng_softc *)self;
    	struct usb_attach_arg *uaa = aux;
    	struct usbd_interface *iface = uaa->iface;
    	usb_interface_descriptor_t *id;
    	usb_endpoint_descriptor_t *ed;
    	int ep_ibulk = -1, ep_obulk = -1;
    	usbd_status err;
    	int i;
    
    	sc->sc_udev = uaa->device;
    	sc->sc_dtr = -1;
    	sc->sc_rts = -1;
    	sc->sc_first_run = 1;
    
    	usb_init_task(&sc->sc_task, uonerng_task, sc, USB_TASK_TYPE_GENERIC);
    
    	/* locate the control interface number and the data interface */
    	err = usbd_device2interface_handle(sc->sc_udev,
    	    ONERNG_IFACE_CTRL_INDEX, &iface);
    	if (err || iface == NULL) {
    		printf("%s: failed to locate control interface, err=%s\n",
    		    DEVNAME(sc), usbd_errstr(err));
    		goto fail;
    	}
    	id = usbd_get_interface_descriptor(iface);
    	if (id != NULL &&
    	    id->bInterfaceClass == UICLASS_CDC &&
    	    id->bInterfaceSubClass == UISUBCLASS_ABSTRACT_CONTROL_MODEL &&
    	    id->bInterfaceProtocol == UIPROTO_CDC_AT) {
    		sc->sc_ctl_iface_no = id->bInterfaceNumber;
    	} else {
    		printf("%s: control interface number not found\n",
    		    DEVNAME(sc));
    		goto fail;
    	}
    
    	err = usbd_device2interface_handle(sc->sc_udev,
    	    ONERNG_IFACE_DATA_INDEX, &sc->sc_data_iface);
    	if (err || sc->sc_data_iface == NULL) {
    		printf("%s: failed to locate data interface, err=%s\n",
    		    DEVNAME(sc), usbd_errstr(err));
    		goto fail;
    	}
    
    	/* Find the bulk endpoints */
    	id = usbd_get_interface_descriptor(sc->sc_data_iface);
    	if (id == NULL ||
    	    id->bInterfaceClass != UICLASS_CDC_DATA ||
    	    id->bInterfaceSubClass != UISUBCLASS_DATA) {
    		printf("%s: no data interface descriptor\n", DEVNAME(sc));
    		goto fail;
    	}
    	for (i = 0; i < id->bNumEndpoints; i++) {
    		ed = usbd_interface2endpoint_descriptor(sc->sc_data_iface, i);
    		if (ed == NULL) {
    			printf("%s: no endpoint descriptor for %d\n",
    			    DEVNAME(sc), i);
    			goto fail;
    		}
    		if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN &&
    		    UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
                            ep_ibulk = ed->bEndpointAddress;
                    } else if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT &&
    			   UE_GET_XFERTYPE(ed->bmAttributes) == UE_BULK) {
                            ep_obulk = ed->bEndpointAddress;
                    }
            }
    
    	if (ep_ibulk == -1) {
    		printf("%s: Could not find data bulk in\n", DEVNAME(sc));
    		goto fail;
    	}
    	if (ep_obulk == -1) {
    		printf("%s: Could not find data bulk out\n", DEVNAME(sc));
    		goto fail;
    	}
    
    	/* Open pipes */
    	err = usbd_open_pipe(sc->sc_data_iface, ep_ibulk,
    	    USBD_EXCLUSIVE_USE, &sc->sc_inpipe);
    	if (err) {
    		printf("%s: failed to open bulk-in pipe: %s\n",
    		    DEVNAME(sc), usbd_errstr(err));
    		goto fail;
    	}
    	err = usbd_open_pipe(sc->sc_data_iface, ep_obulk,
    	    USBD_EXCLUSIVE_USE, &sc->sc_outpipe);
    	if (err) {
    		printf("%s: failed to open bulk-out pipe: %s\n",
    		    DEVNAME(sc), usbd_errstr(err));
    		goto fail;
    	}
    
    	/* Allocate xfer/buffer for bulk transfers */
    	sc->sc_xfer = usbd_alloc_xfer(sc->sc_udev);
    	if (sc->sc_xfer == NULL) {
    		printf("%s: could not alloc xfer\n", DEVNAME(sc));
    		goto fail;
    	}
    	sc->sc_buf = usbd_alloc_buffer(sc->sc_xfer, ONERNG_BUFSIZ);
    	if (sc->sc_buf == NULL) {
    		printf("%s: could not alloc %d-byte buffer\n", DEVNAME(sc),
    		    ONERNG_BUFSIZ);
    		goto fail;
    	}
    
    	if (uonerng_enable(sc) != 0) {
    		goto fail;
    	}
    
    	timeout_set(&sc->sc_timeout, uonerng_timeout, sc);
    
    	/* get the initial random data as early as possible */
    	uonerng_task(sc);
    
    	usb_add_task(sc->sc_udev, &sc->sc_task);
    	return;
    
     fail:
    	usbd_deactivate(sc->sc_udev);
    	uonerng_cleanup(sc);
    }
    
    int
    uonerng_enable(struct uonerng_softc *sc)
    {
    	int err;
    
    	if ((err = uonerng_rts(sc, 0))) {
    		printf("%s: failed to clear RTS: %s\n", DEVNAME(sc),
    		    usbd_errstr(err));
    		return (1);
    	}
    
    	usbd_setup_xfer(sc->sc_xfer, sc->sc_outpipe, sc,
    	    ONERNG_AVALANCHE_WHITENER, sizeof(ONERNG_AVALANCHE_WHITENER),
    	    USBD_SYNCHRONOUS, ONERNG_TIMEOUT, NULL);
    	if ((err = usbd_transfer(sc->sc_xfer))) {
    		printf("%s: failed to set operating mode: %s\n",
    		    DEVNAME(sc), usbd_errstr(err));
    		return (1);
    	}
    
    	usbd_setup_xfer(sc->sc_xfer, sc->sc_outpipe, sc,
    	    ONERNG_OP_ENABLE, sizeof(ONERNG_OP_ENABLE),
    	    USBD_SYNCHRONOUS, ONERNG_TIMEOUT, NULL);
    	if ((err = usbd_transfer(sc->sc_xfer))) {
    		printf("%s: failed to enable device: %s\n",
    		    DEVNAME(sc), usbd_errstr(err));
    		return (1);
    	}
    
    	return (0);
    }
    
    int
    uonerng_detach(struct device *self, int flags)
    {
    	struct uonerng_softc *sc = (struct uonerng_softc *)self;
    
    	usb_rem_task(sc->sc_udev, &sc->sc_task);
    	if (timeout_initialized(&sc->sc_timeout)) {
    		timeout_del(&sc->sc_timeout);
    	}
    	uonerng_cleanup(sc);
    	return (0);
    }
    
    void
    uonerng_cleanup(struct uonerng_softc *sc)
    {
    	if (sc->sc_inpipe != NULL) {
    		usbd_close_pipe(sc->sc_inpipe);
    		sc->sc_inpipe = NULL;
    	}
    	if (sc->sc_outpipe != NULL) {
    		usbd_close_pipe(sc->sc_outpipe);
    		sc->sc_outpipe = NULL;
    	}
    
    	/* usbd_free_xfer will also free the buffer if necessary */
    	if (sc->sc_xfer != NULL) {
    		usbd_free_xfer(sc->sc_xfer);
    		sc->sc_xfer = NULL;
    	}
    }
    
    usbd_status
    uonerng_rts(struct uonerng_softc *sc, int onoff)
    {
    	if (sc->sc_rts == onoff)
    		return USBD_NORMAL_COMPLETION;
    	sc->sc_rts = onoff;
    
    	return uonerng_set_line_state(sc);
    }
    
    usbd_status
    uonerng_set_line_state(struct uonerng_softc *sc)
    {
    	usb_device_request_t req;
    	int ls;
    
    	ls = (sc->sc_dtr ? UCDC_LINE_DTR : 0) |
    	     (sc->sc_rts ? UCDC_LINE_RTS : 0);
    	req.bmRequestType = UT_WRITE_CLASS_INTERFACE;
    	req.bRequest = UCDC_SET_CONTROL_LINE_STATE;
    	USETW(req.wValue, ls);
    	USETW(req.wIndex, sc->sc_ctl_iface_no);
    	USETW(req.wLength, 0);
    
    	return usbd_do_request(sc->sc_udev, &req, 0);
    }
    
    void
    uonerng_task(void *arg)
    {
    	struct uonerng_softc *sc = (struct uonerng_softc *) arg;
    	usbd_status error;
    	u_int32_t len, int_count, i;
    #ifdef ONERNG_MEASURE_RATE
    	time_t elapsed;
    	int rate;
    #endif
    
    	usbd_setup_xfer(sc->sc_xfer, sc->sc_inpipe, NULL, sc->sc_buf,
    	    ONERNG_BUFSIZ,
    	    USBD_SHORT_XFER_OK | USBD_SYNCHRONOUS | USBD_NO_COPY,
    	    ONERNG_TIMEOUT, NULL);
    	error = usbd_transfer(sc->sc_xfer);
    	if (error) {
    		printf("%s: xfer failed: %s\n", DEVNAME(sc),
    		    usbd_errstr(error));
    		goto bail;
    	}
    	usbd_get_xfer_status(sc->sc_xfer, NULL, NULL, &len, NULL);
    	if (len < sizeof(int)) {
    		printf("%s: xfer too short (%u bytes) - dropping\n",
    		    DEVNAME(sc), len);
    		goto bail;
    	}
    
    #ifdef ONERNG_MEASURE_RATE
    	if (sc->sc_first_run) {
    		sc->sc_counted_bytes = 0;
    		getmicrotime(&(sc->sc_start));
    	}
    	sc->sc_counted_bytes += len;
    	getmicrotime(&(sc->sc_cur));
    	elapsed = sc->sc_cur.tv_sec - sc->sc_start.tv_sec;
    	if (elapsed >= ONERNG_RATE_SECONDS) {
    		rate = (8 * sc->sc_counted_bytes) / (elapsed * 1024);
    		printf("%s: transfer rate = %d kb/s\n", DEVNAME(sc), rate);
    
    		/* set up for next measurement */
    		sc->sc_counted_bytes = 0;
    		getmicrotime(&(sc->sc_start));
    	}
    #endif
    
    	int_count = len / sizeof(int);
    	for (i = 0; i < int_count; i++) {
    		enqueue_randomness(sc->sc_buf[i]);
    	}
    bail:
    
    	if (sc->sc_first_run) {
    		sc->sc_first_run = 0;
    	} else {
    		timeout_add_msec(&sc->sc_timeout, ONERNG_MSECS);
    	}
    }
    
    void
    uonerng_timeout(void *arg)
    {
    	struct uonerng_softc *sc = arg;
    
    	usb_add_task(sc->sc_udev, &sc->sc_task);
    }