[FIDO]U2FHID协议

转自: http://blog.sina.com.cn/s/blog_625033800102vzag.html

FIDO(Fast IDentity Online)联盟成立于2012年,FIDO联盟通过定义出一套开放、可扩展、可协同的技术规范,来改变现有在线认证方式,减少认证用户时对密码(password)的依赖。FIDO有两套规范:U2F和UAF。

无密码的UAF(Universal Authentication Framework

  • 用户携带含有UAF的客户设备
  • 用户出示一个本地的生物识别特征或者PIN
  • 网站额可以选择是否保存密码

用户选择一个本地的认证方案(例如按一下指纹、看一下摄像头、对麦克说话,输入一个PIN等)把他的设备注册到在线服务上去。只需要一次注册,之后用户再需要去认证时,就可以简单的重复一个认证动作即可。用户在进行身份认证时,不在需要输入他们的密码了。UAF也允许组合多种认证方案,比如指纹+PIN。


第二因子的U2F(Universal 2nd Factor) 

  • 用户携带U2F设备,浏览器支持这个设备
  • 用户出示U2F设备
  • 网站可以使用简单的密码(比如4个数字的PIN)

FIDO U2F认证,国内的文章一般翻译成FIDO两步认证。U2F是在现有的用户名+密码认证的基础之上,增加一个更安全的认证因子用于登录认证。用户可以像以前一样通过用户名和密码登录服务,服务会提示用户出示一个第二因子设备来进行认证。U2F可以使用简单的密码(比如4个数字的PIN)而不牺牲安全性。

U2F出示第二因子的形式一般是按一下USB设备上的按键或者放入NFC。


UAF先放一边,U2F的工作流程比较简单,具体的可以看FIDO联盟官网。下面主要说下U2F HID协议。

首先要明确一下U2FHID协议不是U2F的应用层协议,是描述U2F的消息如何通过HID传输的底层协议,U2F应用层的协议在U2F Raw Message中定义。U2FHID协议可以支持在大多数平台上直接使用而不需要安装驱动,可以支持多应用并发访问设备。

并发和通道

U2FHID设备处理多客户端,比如多个应用通过HID栈访问单个资源,每个客户端都可以和U2FHID设备通过一个逻辑通道(logical channel)进行通讯,每个客户端都使用一个唯一的32bit通道ID来判断用途通道ID由U2F设备来进行分配,确保唯一产生通道ID的算法由U2FHID设备的厂商规范定义,FIDO的U2FHID协议中不进行定义

通道ID 0是保留的,0xFFFFFFFF也是保留给广播命令的。

消息和包结构

包(Packets)分为两类,初始化包(initialization packets)和附加包(continuation packets)。就像initialization packets这个名字一样,每个应用层消息的第一包都是initialization packet,也是一个事物的开始。如果整个应用层消息不能通过一个包下发,就需要一个或者多个continuation packet来发送了,直到把所有消息发完。

一个应用层消息从主机发送到设备叫做请求(request),从设备返回给主机的叫做响应(response)。请求和响应消息是相同的结构,一个事勿从一个请求的initialization packet开始,截止于一个响应的最后一个包。包的长度永远是固定的大小,一个包中的有的字节并没有被使用到,没有使用的字节需要设置为0。

初始化包的定义

偏移长度名称描述
04CID通道ID
41CMD命令ID
51BCNTH发送数据长度的高位
61BCNTL发送数据长度的低位
7(s-7)DATA发送数据(s等于包的固定长度)

 附加包的定义

偏移长度名称描述
04CID通道ID
41SEQ包的顺序,0x00..0x7F
5(s-5)DATA发送数据(s等于包的固定长度)

如果一个应用层消息长度小于等于s-7,那么一个包就可以发完,如果一个比较大的应用层消息,需要拆分成一个或者多个附加包,第一个附加包的SEQ为0,每次附加包的SEQ值增加1,最大到0xFF。 USB全速设备的包长度的最大值是64字节,这样最大的一个应用层消息可以发送的长度就是,64-7+128*(64-5)=7609字节。

下面是一个初始化包和附加包的C的定义

typedef struct {
  uint32_t cid;                        // Channel identifier
  union {
    uint8_t type;                      // Frame type - b7 defines type
    struct {
      uint8_t cmd;                     // Command - b7 set
      uint8_t bcnth;                   // Message byte count - high part
      uint8_t bcntl;                   // Message byte count - low part
      uint8_t data[HID_RPT_SIZE - 7];  // Data payload
    } init;
    struct {
      uint8_t seq;                     // Sequence number - b7 cleared
      uint8_t data[HID_RPT_SIZE - 5];  // Data payload
    } cont;
  };
} U2FHID_FRAME;

同步

搞USBKEY就不能不说到同步,U2FHID也一样,一个事务永远分为三个阶段:消息从主机发送到设备,设备处理消息,响应从设备返回到主机,U2FHID的事务必须是原子的,一个事务一旦开始,不能被其他应用中断。

U2FHID 命令

还是要明确一下,下面介绍的是U2FHID协议中的命令,不是U2F应用层的命令,U2F应用层的命令在U2F Raw Message中定义,并使用U2F_MSG命令发送。

1. U2FHID_MSG

这个指令是用来发送U2F应用层消息的到FIDO设备的指令,数据域data中的值就是U2F的应用层指令。

2.U2FHID_INIT

这个指令是应用请求FIDO设备分配一个唯一的32bit的CID,这个CID将会在应用剩下的时间中使用到。请求FIDO设备分配一个新的逻辑通道,请求的应用需要使用广播通道U2FHID_BROADCAST_CID,设备会使用广播通道在响应中重新返回一个通道ID。

3.U2FHID_PING

用来调试用的,发送一个事务到设备。

4.U2FHID_ERROR

只用于响应消息。

5.U2FHID_WINK

这个指令是可选的,如果设备有LED灯,可以让灯闪一下或者其他类似的行为,主要是用于提示用户。


可以看出来,U2FHID协议最基本的就是使用上面的U2FHID_FRAME这个结构来和FIDO设备进行通讯,下面这个函数就是把不同的U2FHID指令和数据域封装成U2FHID_FRAME的格式下发到USB设备中。

u2fh_rc
u2fh_sendrecv (u2fh_devs * devs, unsigned index, uint8_t cmd,
        const unsigned char *send, uint16_t sendlen,
        unsigned char *recv, size_t * recvlen)
{
    int datasent = 0;
    int sequence = 0;
    struct u2fdevice *dev;

    if (index >= devs->num_devices || !devs->devs[index].is_alive)
    {
        return U2FH_NO_U2F_DEVICE;
    }

    dev = &devs->devs[index];

    while (sendlen > datasent)
    {
        U2FHID_FRAME frame = { 0 };
        {
            int len = sendlen - datasent;
            int maxlen;
            unsigned char *data;
            frame.cid = dev->cid;
            if (datasent == 0)
            {
                frame.init.cmd = cmd;
                frame.init.bcnth = (sendlen >> 8) & 0xff;
                frame.init.bcntl = sendlen & 0xff;
                data = frame.init.data;
                maxlen = sizeof (frame.init.data);
            }
            else
            {
                frame.cont.seq = sequence++;
                data = frame.cont.data;
                maxlen = sizeof (frame.cont.data);
            }
            if (len > maxlen)
            {
                len = maxlen;
            }
            memcpy (data, send + datasent, len);
            datasent += len;
        }

        {
            unsigned char data[sizeof (U2FHID_FRAME) + 1];
            int len;
            data[0] = 0;
            memcpy (data + 1, &frame, sizeof (U2FHID_FRAME));
            if (debug)
            {
                fprintf (stderr, "USB send: ");
                dumpHex (data, 0, sizeof (U2FHID_FRAME));
            }

            len = hid_write (dev->devh, data, sizeof (U2FHID_FRAME) + 1);
            if (debug)
                fprintf (stderr, "USB write returned %d\n", len);
            if (len < 0)
                return U2FH_TRANSPORT_ERROR;
            if (sizeof (U2FHID_FRAME) + 1 != len)
                return U2FH_TRANSPORT_ERROR;
        }
    }

    {
        U2FHID_FRAME frame;
        unsigned char data[HID_RPT_SIZE];
        int len = HID_RPT_SIZE;
        int maxlen = *recvlen;
        int recvddata = 0;
        short datalen;
        int timeout = HID_TIMEOUT;
        int rc = 0;

        while (rc == 0)
        {
            if (debug)
            {
                fprintf (stderr, "now trying with timeout %d\n", timeout);
            }
            rc = hid_read_timeout (dev->devh, data, len, timeout);
            timeout *= 2;
            if (timeout > HID_MAX_TIMEOUT)
            {
                rc = -2;
                break;
            }
        }
        sequence = 0;

        if (debug)
        {
            fprintf (stderr, "USB read rc read %d\n", len);
            if (rc > 0)
            {
                fprintf (stderr, "USB recv: ");
                dumpHex (data, 0, rc);
            }
        }
        if (rc < 0)
        {
            return U2FH_TRANSPORT_ERROR;
        }

        memcpy (&frame, data, HID_RPT_SIZE);
        if (frame.cid != dev->cid || frame.init.cmd != cmd)
        {
            return U2FH_TRANSPORT_ERROR;
        }
        datalen = frame.init.bcnth << 8 | frame.init.bcntl;
        if (datalen + datalen % HID_RPT_SIZE > maxlen)
        {
            return U2FH_TRANSPORT_ERROR;
        }
        memcpy (recv, frame.init.data, sizeof (frame.init.data));
        recvddata = sizeof (frame.init.data);

        while (datalen > recvddata)
        {
            timeout = HID_TIMEOUT;
            rc = 0;
            while (rc == 0)
            {
                if (debug)
                {
                    fprintf (stderr, "now trying with timeout %d\n", timeout);
                }
                rc = hid_read_timeout (dev->devh, data, len, timeout);
                timeout *= 2;
                if (timeout > HID_MAX_TIMEOUT)
                {
                    rc = -2;
                    break;
                }
            }
            if (debug)
            {
                fprintf (stderr, "USB read rc read %d\n", len);
                if (rc > 0)
                {
                    fprintf (stderr, "USB recv: ");
                    dumpHex (data, 0, rc);
                }
            }
            if (rc < 0)
            {
                return U2FH_TRANSPORT_ERROR;
            }

            memcpy (&frame, data, HID_RPT_SIZE);
            if (frame.cid != dev->cid || frame.cont.seq != sequence++)
            {
                fprintf (stderr, "bar: %d %d %d %d\n", frame.cid, dev->cid,
                        frame.cont.seq, sequence);
                return U2FH_TRANSPORT_ERROR;
            }
            memcpy (recv + recvddata, frame.cont.data, sizeof (frame.cont.data));
            recvddata += sizeof (frame.cont.data);
        }
        *recvlen = datalen;
    }
    return U2FH_OK;
}






HID 设备类读写 #include //! Defines the maximum length of a serial number #define SERNUM_LEN 40 //! Defines the maximum number of physical devices #define MAX_PHYS_DEVICES 6 //! \name HID Device return codes //! @{ // //! HID action/transfer was successful #define HID_DEVICE_SUCCESS 0x00 //! HID device was not found #define HID_DEVICE_NOT_FOUND 0x01 //! HID device is not opened #define HID_DEVICE_NOT_OPENED 0x02 //! HID device is allready opened #define HID_DEVICE_ALREADY_OPENED 0x03 //! Timeout occurs during transfer #define HID_DEVICE_TRANSFER_TIMEOUT 0x04 //! HID transfer failed #define HID_DEVICE_TRANSFER_FAILED 0x05 //! Invalid handle #define HID_DEVICE_HANDLE_ERROR 0x06 //! Unknown error #define HID_DEVICE_UNKNOWN_ERROR 0xFF //! @} // Enabled only when debugging HID connection issues //#define DEBUG_MODE //****************************************************************************** // //! \brief Device information structure. // //****************************************************************************** struct strHidDevice{ //! Handle for hid device HANDLE hndHidDevice; //! Indicator if device is opened BOOL bDeviceOpen; //! Timeout for GetReport requests UINT uGetReportTimeout; //! Timeout for SetReport requests UINT uSetReportTimeout; //! Asynchronous I/O structure OVERLAPPED oRead; //! Asynchronous I/O structure OVERLAPPED oWrite; //! Maximum length of InReport's WORD wInReportBufferLength; //! Maximum length of OutReport's WORD wOutReportBufferLength; //! InBuffer contains data, if InReport provides more data then the application actual need BYTE inBuffer[8192]; //! Number of current used bytes in inBuffer WORD inBufferUsed; }; //****************************************************************************** // //! A structure that tracks the number of serial numbers // //****************************************************************************** struct strTrackSerialNumbers { //! Index number DWORD deviceNum; //! Serial number of physical device char serialNum[SERNUM_LEN]; }; //****************************************************************************** // //! \addtogroup hiddevice_api //! @{ // //****************************************************************************** //****************************************************************************** // //! \brief Close a HID Device. //! //! This function will close a HID device based on the HID structure //! //! \param pstrHidDevice Structure which contains important data of an HID //! device //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_OPENED //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_Close(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief Flush USB buffer for the given device //! //! \param pstrHidDevice Structure which contains important data of an HID //! device. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR //! \n \b HID_DEVICE_UNKNOWN_ERROR // //****************************************************************************** BYTE HID_FlushBuffer(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief Gets the number of HID devices //! //! This function will return the number of interfaces connected with a //! specified VID, PID and serial number, if no devices are connected, //! it will return a 0 //! //! \param vid Vendor-Id of the device //! \param pid Product-Id of the device //! \param numSerNums Total number of connected physical devices //! //! \return Return the number of connected devices with the specific VID, PID, //! and serial number. // //****************************************************************************** DWORD HID_GetNumOfInterfaces(WORD vid, WORD pid, DWORD numSerNums); //****************************************************************************** // //! \brief Gets the number of serial number and serial number list //! //! Scans the HID Devices on the system for any whose VID/PID match the //! ones specified. For every one it finds, it returns that device's //! serial number in serialNumList. Every physical USB device within a //! given VID/PID space has a unique serial number; therefore, each //! item in the list corresponds with a separate physical USB device //! attached to this host; that is, different physical instances of the //! same product or design. The function returns the total number of //! serial numbers found; if none are found, it returns 0. //! //! \param vid Vendor-ID of the device //! \param pid Product-ID of the device //! \param serialNumList List of serial numbers corresponding to the passed //! VID and PID //! //! \return Returns the number of connected physical devices with the specific //! VID and PID // //****************************************************************************** DWORD HID_GetSerNums(WORD vid, WORD pid, struct strTrackSerialNumbers * serialNumList); //****************************************************************************** // //! \brief Returns the version number of a device. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device. //! \param VersionNumber Pointer to USHORT variable. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_GetVersionNumber(struct strHidDevice* pstrHidDevice, USHORT * VersionNumber); //****************************************************************************** // //! \brief Init structure with default values. //! //! It is important to call HID_Init() before calling HID_Open() to //! avoid unpredictable behavoir. //! //! \param pstrHidDevice Structure which contains important data of a HID //! device //! //! \return None // //****************************************************************************** void HID_Init(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief This has to be called inside WM_ON_DEVICECHANGE notification window //! //! This function checks if the particular HID device structure is //! still connected or disconnected. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BOOL HID_IsDeviceAffected(struct strHidDevice* pstrHidDevice); //****************************************************************************** // //! \brief Open a HID Device. //! //! This function opens the HID device associated with the HID interface //! 'deviceIndex' (0-7), on the physical device described by the VID, //! PID, and serial number. //! \param pstrHidDevice Structure which contains important data of an HID //! device //! \param vid Vendor-ID of the device //! \param pid Product-ID of the device //! \param deviceIndex Index of the device.If only one HID is connected, //! deviceIndex is 0. //! - Starts with zero //! - Maximum value is (HID_GetNumOfInterfaces() - 1) //! \param serialNumber Serial number of device to be opened. //! \param totalDevNum Total number of interfaces associated with the //! serial number //! \param totalSerNum Total number of physical devices associated with //! the VID/PID //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_FOUND //! \n \b HID_DEVICE_ALREADY_OPENED // //****************************************************************************** BYTE HID_Open(struct strHidDevice* pstrHidDevice, WORD vid, WORD pid, DWORD deviceIndex, char serialNumber[SERNUM_LEN], DWORD totalDevNum, DWORD totalSerNum); //****************************************************************************** // //! \brief Reads a data stream from the given HID device. //! //! Prefixed report ID will be skipped. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device //! \param buffer Pointer to buffer in which will be written //! \param bufferSize Number of bytes to read //! \param bytesReturned Number of actual read bytes //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_OPENED //! \n \b HID_DEVICE_TRANSFER_TIMEOUT //! \n \b HID_DEVICE_TRANSFER_FAILED // //****************************************************************************** BYTE HID_ReadFile(struct strHidDevice* pstrHidDevice, BYTE* buffer, DWORD bufferSize, DWORD* bytesReturned); //****************************************************************************** // //! \brief Registers a device for program Windows notification. //! //! Registers the window pointed to by handle hWnd to receive //! notification when devices are added or removed from the system. //! //! \param hWnd Windows handle //! \param diNotifyHandle Device notification handle pointer address //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_RegisterForDeviceNotification(HWND hWnd, HDEVNOTIFY* diNotifyHandle); //****************************************************************************** // //! \brief Un-Registers a device from Windows notification. //! //! Un-registers the window pointed to by handle hWnd to receive //! notification when devices are added or removed from the system. //! //! \param diNotifyHandle: Device notification handle pointer address. //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_HANDLE_ERROR // //****************************************************************************** BYTE HID_UnRegisterForDeviceNotification(HDEVNOTIFY* diNotifyHandle); //****************************************************************************** // //! \brief Writes a data stream to the given HID device. //! //! Needed report IDs will be generated automatically. //! //! \param pstrHidDevice Structure which contains important data of an HID //! device //! \param buffer Buffer which will be send //! \param bufferSize Number of bytes to send //! //! \return Returns the error status, as one of //! \n \b HID_DEVICE_SUCCESS //! \n \b HID_DEVICE_NOT_OPENED //! \n \b HID_DEVICE_TRANSFER_TIMEOUT //! \n \b HID_DEVICE_TRANSFER_FAILED // //****************************************************************************** BYTE HID_WriteFile(struct strHidDevice* pstrHidDevice, BYTE* buffer, DWORD bufferSize);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值