Last active
August 29, 2022 06:43
-
-
Save hackerb9/4abd1484c48c857b4c4b73f4fc9c8e7d to your computer and use it in GitHub Desktop.
Detect U2F (FIDO2) USB security keys by reading the hidraw report descriptor
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Was originally u2f_hidraw_id.c, | |
* Copyright (c) 2014-2015 Andrew Lutomirski | |
* | |
* Mangled into isu2f.c by hackerb9 in 2022 as a demonstration for firejail. | |
* | |
* This program is free software; you can redistribute it and/or | |
* modify it under the terms of the GNU Lesser General Public | |
* License as published by the Free Software Foundation; either | |
* version 2 of the License, or (at your option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, | |
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
* Library General Public License for more details. | |
* | |
* You should have received a copy of the GNU General Public License | |
* along with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#include <stdio.h> /* fprintf() */ | |
#include <string.h> /* strlen() */ | |
#include <fcntl.h> /* open() */ | |
#include <unistd.h> /* read(), close() */ | |
#include <linux/hid.h> /* HID_MAX_DESCRIPTOR_SIZE */ | |
#define HID_RPTDESC_FIRST_BYTE_LONG_ITEM 0xfe | |
#define HID_RPTDESC_TYPE_GLOBAL 0x1 | |
#define HID_RPTDESC_TYPE_LOCAL 0x2 | |
#define HID_RPTDESC_GLOBAL_ITEM_USAGE_PAGE 0x0 | |
#define HID_RPTDESC_LOCAL_ITEM_USAGE 0x0 | |
int main(int argc, char **argv) | |
{ | |
char *path = NULL; | |
unsigned char desc[HID_MAX_DESCRIPTOR_SIZE]; | |
int desclen; | |
int fd = -1; | |
int i; | |
int ret = 1; | |
unsigned int usage_page = 0; | |
int is_u2f_token = 0; | |
if (argc < 2) { | |
fprintf(stderr, "Usage: isu2f /sys/class/hidraw/*/device/report_descriptor\n"); | |
return 1; | |
} | |
while (*(++argv)) { | |
path = *argv; | |
is_u2f_token = 0; | |
fd = open(path, O_RDONLY | O_NOFOLLOW); | |
if (fd == -1) { | |
perror(path); | |
continue; | |
} | |
desclen = read(fd, desc, sizeof(desc)); | |
if (desclen < 0) { | |
perror(path); | |
continue; | |
/* XXX Maybe should handle EINTR or short reads */ | |
} | |
if (desclen == 0) { | |
fprintf(stderr, "%s: Empty\n", path); | |
continue; | |
} | |
/* Parse the report descriptor. */ | |
for (i = 0; i < desclen; ) { | |
/* | |
* The first byte of the report descriptor is a tag, a type, | |
* and a code that helps determine the size. | |
*/ | |
unsigned char tag = desc[i] >> 4; | |
unsigned char type = (desc[i] >> 2) & 0x3; | |
unsigned char sizecode = desc[i] & 0x3; | |
int size, j; | |
unsigned int value = 0; | |
if (desc[i] == HID_RPTDESC_FIRST_BYTE_LONG_ITEM) { | |
/* Long item; skip it. */ | |
if (i + 1 >= desclen) { | |
fprintf(stderr, "%s: Bad report descriptor: EOF when expecting long_item length.\n", path); | |
break; | |
} | |
i += (desc[i+1] + 3); /* Can't overflow. */ | |
continue; | |
} | |
size = (sizecode < 3 ? sizecode : 4); | |
if (i + 1 + size > desclen) { | |
fprintf(stderr, "%s: Bad report descriptor: EOF when expecting item length.\n", path); | |
i += 1 + size; | |
continue; | |
} | |
for (j = 0; j < size; j++) | |
value |= (desc[i + 1 + j] << 8*j); | |
if (type == HID_RPTDESC_TYPE_GLOBAL && | |
tag == HID_RPTDESC_GLOBAL_ITEM_USAGE_PAGE) | |
usage_page = value; | |
/* | |
* Detect U2F tokens. See: | |
* https://fidoalliance.org/specs/fido-u2f-HID-protocol-v1.0-rd-20141008.pdf | |
* http://www.usb.org/developers/hidpage/HUTRR48.pdf | |
*/ | |
if (type == HID_RPTDESC_TYPE_LOCAL && | |
tag == HID_RPTDESC_LOCAL_ITEM_USAGE) { | |
if (usage_page == 0xf1d0 && value == 0x1) { | |
is_u2f_token = 1; | |
break; | |
} | |
} | |
i += 1 + size; | |
} | |
/* Fit in 80 cols: lop off "/report_descriptor" */ | |
int l=strlen(path); | |
if (l>18 && (strcmp(path+l-18, "/report_descriptor") == 0)) | |
path[l-18] = '\0'; | |
printf("%s: %s\n", path, | |
(is_u2f_token)? | |
"Yup, this is a U2F_TOKEN": | |
"Nope, not a U2F_TOKEN"); | |
ret = 0; | |
if (fd != -1) | |
close(fd); | |
} /* Matches 'while (++argv) {...' */ | |
if (fd != -1) | |
close(fd); | |
return ret; | |
} |
Note 2: While only tested with USB, this would likely work with bluetooth as well since it uses the same HID format.
Compilation:
cc -o isu2f isu2f.c
Usage
isu2f /sys/class/hidraw/*/device/report_descriptor
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note: This returns true to the shell if any U2F keys were found. False, if none are plugged in.