yubikey 0.2.0

A library to interact with YubiKeys, developed at Coturnix.
use std::time::Duration;
use libusb::{request_type, Direction, RequestType, Recipient};
use super::*;
use std;
use util::*;

bitflags! {
    pub flags Flags: u8 {
        const SLOT_WRITE_FLAG = 0x80,
        const RESP_PENDING_FLAG = 0x40
    }
}

const SLOT_DATA_SIZE: usize = 64;
const HID_GET_REPORT: u8 = 0x01;
const HID_SET_REPORT: u8 = 0x09;
const REPORT_TYPE_FEATURE: u16 = 0x03;

impl<'a> YubiKeyHandle<'a> {
    /// Wait until a condition is satisfied.
    pub fn wait<F: Fn(Flags) -> bool>(&mut self, f: F, buf: &mut [u8]) -> Result<(), Error> {
        loop {
            try!(self.read(buf));
            
            let flags = Flags::from_bits_truncate(buf[7]);
            if flags.contains(SLOT_WRITE_FLAG) || flags.is_empty() {
                self.version = Some((Version {
                    major: buf[1],
                    minor: buf[2],
                    build: buf[3],
                },
                                     buf[4]))
            }
            if f(flags) {
                return Ok(());
            }
            std::thread::sleep(Duration::new(0, 1000000));
        }
    }

    /// Read 8 bytes (`buf` should be 8 bytes long).
    pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        assert_eq!(buf.len(), 8);
        let reqtype = request_type(Direction::In, RequestType::Class, Recipient::Interface);
        let value = REPORT_TYPE_FEATURE << 8;
        Ok(try!(self.h.read_control(reqtype, HID_GET_REPORT, value, 0, buf, Duration::new(2, 0))))
    }

    /// Write a frame, split into 10 packets of length 7.
    pub fn write_frame(&mut self, frame: &Frame) -> Result<(), Error> {

        let mut data =
            unsafe { std::slice::from_raw_parts(frame as *const Frame as *const u8, 70) };

        let mut seq = 0;
        let mut buf = [0; 8];
        while !data.is_empty() {

            let (a, b) = data.split_at(7);

            if seq == 0 || b.is_empty() || a.iter().any(|&x| x != 0) {

                let mut packet = [0; 8];

                (&mut packet[ .. 7 ]).copy_from_slice(a);

                packet[7] = SLOT_WRITE_FLAG.bits() + seq;

                try!(self.wait(|x| !x.contains(SLOT_WRITE_FLAG), &mut buf));
                try!(self.raw_write(&packet))
            }
            data = b;
            seq += 1
        }
        Ok(())
    }

    /// A wrapper around `write_control`.
    pub fn raw_write(&mut self, packet: &[u8]) -> Result<(), Error> {
        let reqtype = request_type(Direction::Out, RequestType::Class, Recipient::Interface);
        let value = REPORT_TYPE_FEATURE << 8;
        if try!(self.h.write_control(reqtype,
                                     HID_SET_REPORT,
                                     value,
                                     0,
                                     &packet,
                                     Duration::new(2, 0))) != 8 {
            Err(Error::CouldNotWrite)
        } else {
            Ok(())
        }
    }

    /// Reset the write state after a read.
    pub fn write_reset(&mut self) -> Result<(), Error> {
        try!(self.raw_write(&[0, 0, 0, 0, 0, 0, 0, 0x8f]));
        let mut buf = [0; 8];
        try!(self.wait(|x| !x.contains(SLOT_WRITE_FLAG), &mut buf));
        Ok(())
    }

    pub fn read_response(&mut self, response:&mut [u8]) -> Result<usize, Error> {
        let mut r0 = 0;
        try!(self.wait(|f| {
            f.contains(RESP_PENDING_FLAG)
        }, &mut response[.. 8]));
        r0 += 7;
        loop {
            if try!(self.read(&mut response[r0..r0 + 8])) < 8 {
                break;
            }
            let flags = Flags::from_bits_truncate(response[r0 + 7]);
            if flags.contains(RESP_PENDING_FLAG) {
                let seq = response[r0 + 7] & 0b00011111;
                if r0 > 0 && seq == 0 {
                    // If the sequence number is 0, and we have read at
                    // least one packet, stop.
                    break;
                }
            } else {
                break;
            }
            r0 += 7;
        }
        try!(self.write_reset());
        Ok(r0)
    }
}

#[repr(C)]
#[repr(packed)]
pub struct Frame {
    pub payload: [u8; SLOT_DATA_SIZE],
    command: Command,
    crc: u16,
    filler: [u8; 3],
}
pub trait CommandT {
    fn cmd(&self) -> u8;
}

impl Frame {
    pub fn new(command: Command, payload: [u8; 64]) -> Self {
        let mut f = Frame {
            payload: payload,
            command: command,
            crc: 0,
            filler: [0; 3],
        };
        f.crc = crc16(&f.payload).to_le();
        f
    }
}