Skip to content

Commit 8b77234

Browse files
committed
initial working state for Nitrokey#60, updating to upstream fido2 lib
1 parent c6a93da commit 8b77234

6 files changed

Lines changed: 168 additions & 204 deletions

File tree

pynitrokey/cli/_patches.py

Lines changed: 114 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -11,113 +11,117 @@
1111

1212
import sys
1313

14-
## Windows
15-
if sys.platform.startswith("win32"):
16-
import fido2._pyu2f.windows
17-
18-
oldDevAttrFunc = fido2._pyu2f.windows.FillDeviceAttributes
19-
from ctypes import wintypes
20-
import ctypes
21-
22-
fido2._pyu2f.windows.hid.HidD_GetSerialNumberString.restype = wintypes.BOOLEAN
23-
fido2._pyu2f.windows.hid.HidD_GetSerialNumberString.argtypes = [
24-
ctypes.c_void_p,
25-
ctypes.c_void_p,
26-
ctypes.c_ulong,
27-
]
28-
29-
def newDevAttrFunc(device, descriptor):
30-
oldDevAttrFunc(device, descriptor)
31-
buf_ser = ctypes.create_string_buffer(1024)
32-
result = fido2._pyu2f.windows.hid.HidD_GetSerialNumberString(
33-
device, buf_ser, 1024
34-
)
35-
if result:
36-
descriptor.serial_number = ctypes.wstring_at(buf_ser)
37-
38-
fido2._pyu2f.windows.FillDeviceAttributes = newDevAttrFunc
39-
40-
41-
## macOS
42-
if sys.platform.startswith("darwin"):
43-
import fido2._pyu2f.macos
44-
from fido2._pyu2f import base
45-
from fido2._pyu2f.macos import (
46-
iokit,
47-
IO_HID_DEVICE_REF,
48-
GetDeviceIntProperty,
49-
GetDevicePath,
50-
GetDeviceStringProperty,
51-
HID_DEVICE_PROPERTY_VENDOR_ID,
52-
HID_DEVICE_PROPERTY_PRODUCT_ID,
53-
HID_DEVICE_PROPERTY_PRODUCT,
54-
HID_DEVICE_PROPERTY_PRIMARY_USAGE,
55-
HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE,
56-
HID_DEVICE_PROPERTY_REPORT_ID,
57-
cf,
58-
)
59-
60-
HID_DEVICE_PROPERTY_SERIAL_NUMBER = b"SerialNumber"
61-
62-
def newEnumerate():
63-
"""See base class."""
64-
# Init a HID manager
65-
hid_mgr = iokit.IOHIDManagerCreate(None, None)
66-
if not hid_mgr:
67-
raise OSError("Unable to obtain HID manager reference")
68-
iokit.IOHIDManagerSetDeviceMatching(hid_mgr, None)
69-
70-
# Get devices from HID manager
71-
device_set_ref = iokit.IOHIDManagerCopyDevices(hid_mgr)
72-
if not device_set_ref:
73-
raise OSError("Failed to obtain devices from HID manager")
74-
75-
num = iokit.CFSetGetCount(device_set_ref)
76-
devices = (IO_HID_DEVICE_REF * num)()
77-
iokit.CFSetGetValues(device_set_ref, devices)
78-
79-
# Retrieve and build descriptor dictionaries for each device
80-
descriptors = []
81-
for dev in devices:
82-
d = base.DeviceDescriptor()
83-
d.vendor_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_VENDOR_ID)
84-
d.product_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_PRODUCT_ID)
85-
d.product_string = GetDeviceStringProperty(dev, HID_DEVICE_PROPERTY_PRODUCT)
86-
d.serial_number = GetDeviceStringProperty(
87-
dev, HID_DEVICE_PROPERTY_SERIAL_NUMBER
88-
)
89-
d.usage = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_PRIMARY_USAGE)
90-
d.usage_page = GetDeviceIntProperty(
91-
dev, HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE
92-
)
93-
d.report_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_REPORT_ID)
94-
d.path = GetDevicePath(dev)
95-
descriptors.append(d.ToPublicDict())
96-
97-
# Clean up CF objects
98-
cf.CFRelease(device_set_ref)
99-
cf.CFRelease(hid_mgr)
100-
101-
return descriptors
102-
103-
fido2._pyu2f.macos.MacOsHidDevice.Enumerate = newEnumerate
104-
105-
106-
## Linux
107-
if sys.platform.startswith("linux"):
108-
import fido2._pyu2f.linux
109-
110-
oldnewParseUevent = fido2._pyu2f.linux.ParseUevent
111-
112-
def newParseUevent(uevent, desc):
113-
oldnewParseUevent(uevent, desc)
114-
lines = uevent.split(b"\n")
115-
for line in lines:
116-
line = line.strip()
117-
if not line:
118-
continue
119-
k, v = line.split(b"=")
120-
if k == b"HID_UNIQ":
121-
desc.serial_number = v.decode("utf8")
122-
123-
fido2._pyu2f.linux.ParseUevent = newParseUevent
14+
########################################################
15+
# removed as fido._pyu2f is not part of fido2 anymore...
16+
####################################################
17+
18+
# ## Windows
19+
# if sys.platform.startswith("win32"):
20+
# import fido2._pyu2f.windows
21+
22+
# oldDevAttrFunc = fido2._pyu2f.windows.FillDeviceAttributes
23+
# from ctypes import wintypes
24+
# import ctypes
25+
26+
# fido2._pyu2f.windows.hid.HidD_GetSerialNumberString.restype = wintypes.BOOLEAN
27+
# fido2._pyu2f.windows.hid.HidD_GetSerialNumberString.argtypes = [
28+
# ctypes.c_void_p,
29+
# ctypes.c_void_p,
30+
# ctypes.c_ulong,
31+
# ]
32+
33+
# def newDevAttrFunc(device, descriptor):
34+
# oldDevAttrFunc(device, descriptor)
35+
# buf_ser = ctypes.create_string_buffer(1024)
36+
# result = fido2._pyu2f.windows.hid.HidD_GetSerialNumberString(
37+
# device, buf_ser, 1024
38+
# )
39+
# if result:
40+
# descriptor.serial_number = ctypes.wstring_at(buf_ser)
41+
42+
# fido2._pyu2f.windows.FillDeviceAttributes = newDevAttrFunc
43+
44+
45+
# ## macOS
46+
# if sys.platform.startswith("darwin"):
47+
# import fido2._pyu2f.macos
48+
# from fido2._pyu2f import base
49+
# from fido2._pyu2f.macos import (
50+
# iokit,
51+
# IO_HID_DEVICE_REF,
52+
# GetDeviceIntProperty,
53+
# GetDevicePath,
54+
# GetDeviceStringProperty,
55+
# HID_DEVICE_PROPERTY_VENDOR_ID,
56+
# HID_DEVICE_PROPERTY_PRODUCT_ID,
57+
# HID_DEVICE_PROPERTY_PRODUCT,
58+
# HID_DEVICE_PROPERTY_PRIMARY_USAGE,
59+
# HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE,
60+
# HID_DEVICE_PROPERTY_REPORT_ID,
61+
# cf,
62+
# )
63+
64+
# HID_DEVICE_PROPERTY_SERIAL_NUMBER = b"SerialNumber"
65+
66+
# def newEnumerate():
67+
# """See base class."""
68+
# # Init a HID manager
69+
# hid_mgr = iokit.IOHIDManagerCreate(None, None)
70+
# if not hid_mgr:
71+
# raise OSError("Unable to obtain HID manager reference")
72+
# iokit.IOHIDManagerSetDeviceMatching(hid_mgr, None)
73+
74+
# # Get devices from HID manager
75+
# device_set_ref = iokit.IOHIDManagerCopyDevices(hid_mgr)
76+
# if not device_set_ref:
77+
# raise OSError("Failed to obtain devices from HID manager")
78+
79+
# num = iokit.CFSetGetCount(device_set_ref)
80+
# devices = (IO_HID_DEVICE_REF * num)()
81+
# iokit.CFSetGetValues(device_set_ref, devices)
82+
83+
# # Retrieve and build descriptor dictionaries for each device
84+
# descriptors = []
85+
# for dev in devices:
86+
# d = base.DeviceDescriptor()
87+
# d.vendor_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_VENDOR_ID)
88+
# d.product_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_PRODUCT_ID)
89+
# d.product_string = GetDeviceStringProperty(dev, HID_DEVICE_PROPERTY_PRODUCT)
90+
# d.serial_number = GetDeviceStringProperty(
91+
# dev, HID_DEVICE_PROPERTY_SERIAL_NUMBER
92+
# )
93+
# d.usage = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_PRIMARY_USAGE)
94+
# d.usage_page = GetDeviceIntProperty(
95+
# dev, HID_DEVICE_PROPERTY_PRIMARY_USAGE_PAGE
96+
# )
97+
# d.report_id = GetDeviceIntProperty(dev, HID_DEVICE_PROPERTY_REPORT_ID)
98+
# d.path = GetDevicePath(dev)
99+
# descriptors.append(d.ToPublicDict())
100+
101+
# # Clean up CF objects
102+
# cf.CFRelease(device_set_ref)
103+
# cf.CFRelease(hid_mgr)
104+
105+
# return descriptors
106+
107+
# fido2._pyu2f.macos.MacOsHidDevice.Enumerate = newEnumerate
108+
109+
110+
# ## Linux
111+
# if sys.platform.startswith("linux"):
112+
# import fido2._pyu2f.linux
113+
114+
# oldnewParseUevent = fido2._pyu2f.linux.ParseUevent
115+
116+
# def newParseUevent(uevent, desc):
117+
# oldnewParseUevent(uevent, desc)
118+
# lines = uevent.split(b"\n")
119+
# for line in lines:
120+
# line = line.strip()
121+
# if not line:
122+
# continue
123+
# k, v = line.split(b"=")
124+
# if k == b"HID_UNIQ":
125+
# desc.serial_number = v.decode("utf8")
126+
127+
# fido2._pyu2f.linux.ParseUevent = newParseUevent

pynitrokey/cli/fido2.py

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,12 +281,11 @@ def feedkernel(count, serial):
281281
show_default=True,
282282
)
283283
def make_credential(serial, host, user, udp, prompt):
284-
"""(EXPERIMENTAL) Generate a credential.
284+
"""Generate a credential.
285285
286286
Pass `--prompt ""` to output only the `credential_id` as hex.
287287
"""
288288

289-
local_print("EXPERIMENTAL: use with care, not a fully supported function")
290289
nkfido2.hmac_secret.make_credential(
291290
host=host, user_id=user, serial=serial, output=True, prompt=prompt, udp=udp
292291
)
@@ -309,7 +308,7 @@ def make_credential(serial, host, user, udp, prompt):
309308
@click.argument("credential-id")
310309
@click.argument("challenge")
311310
def challenge_response(serial, host, user, prompt, credential_id, challenge, udp):
312-
"""(EXPERIMENTAL) Uses `hmac-secret` to implement a challenge-response mechanism.
311+
"""Uses `hmac-secret` to implement a challenge-response mechanism.
313312
314313
We abuse hmac-secret, which gives us `HMAC(K, hash(challenge))`, where `K`
315314
is a secret tied to the `credential_id`. We hash the challenge first, since
@@ -323,9 +322,6 @@ def challenge_response(serial, host, user, prompt, credential_id, challenge, udp
323322
The prompt can be suppressed using `--prompt ""`.
324323
"""
325324

326-
local_print("EXPERIMENTAL: Currently disabled: challenge-response")
327-
return
328-
329325
nkfido2.hmac_secret.simple_secret(
330326
credential_id,
331327
challenge,

pynitrokey/fido2/__init__.py

Lines changed: 0 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,6 @@
33
import socket
44
import usb
55

6-
7-
import fido2._pyu2f
8-
import fido2._pyu2f.base
9-
106
from pynitrokey.fido2 import hmac_secret
117
from pynitrokey.fido2.client import NKFido2Client
128
from pynitrokey.exceptions import NoSoloFoundError
@@ -32,60 +28,11 @@ def _UDP_InternalPlatformSwitch(funcname, *args, **kwargs):
3228
return getattr(HidOverUDP, funcname)(*args, **kwargs)
3329

3430

35-
def force_udp_backend():
36-
fido2._pyu2f.InternalPlatformSwitch = _UDP_InternalPlatformSwitch
37-
38-
39-
class HidOverUDP(fido2._pyu2f.base.HidDevice):
40-
@staticmethod
41-
def Enumerate():
42-
a = [
43-
{
44-
"vendor_id": 0x1234,
45-
"product_id": 0x5678,
46-
"product_string": "software test interface",
47-
"serial_number": "12345678",
48-
"usage": 0x01,
49-
"usage_page": 0xF1D0,
50-
"path": "localhost:8111",
51-
}
52-
]
53-
return a
54-
55-
def __init__(self, path):
56-
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
57-
self.sock.bind(("127.0.0.1", 7112))
58-
addr, port = path.split(":")
59-
port = int(port)
60-
self.token = (addr, port)
61-
self.sock.settimeout(1.0)
62-
63-
def GetInReportDataLength(self):
64-
return 64
65-
66-
def GetOutReportDataLength(self):
67-
return 64
68-
69-
def Write(self, packet):
70-
self.sock.sendto(bytearray(packet), self.token)
71-
72-
def Read(self):
73-
msg = [0] * 64
74-
pkt, _ = self.sock.recvfrom(64)
75-
for i, v in enumerate(pkt):
76-
try:
77-
msg[i] = ord(v)
78-
except TypeError:
79-
msg[i] = v
80-
return msg
81-
8231

8332
def find(solo_serial=None, retries=5, raw_device=None, udp=False):
8433
if udp:
8534
force_udp_backend()
8635

87-
88-
8936
p = NKFido2Client()
9037

9138
# This... is not the right way to do it yet

pynitrokey/fido2/client.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -185,18 +185,20 @@ def wink(self,):
185185
def reset(self,):
186186
self.ctap2.reset()
187187

188-
# @todo: unneeded, remove this...
189188
def make_credential(self, pin=None):
189+
client = self.get_current_fido_client()
190190
rp = {"id": self.host, "name": "example site"}
191191
user = {"id": self.user_id, "name": "example user"}
192-
challenge = "Y2hhbGxlbmdl"
193-
attest, data = self.client.make_credential({
194-
"rp": rp,
195-
"user": user,
196-
"challenge": challenge.encode("utf8"),
197-
"pubKeyCredParams": [{"type": "public-key", "alg": -7}],
198-
}, pin=pin)
199-
192+
challenge = b"Y2hhbGxlbmdl"
193+
options = PublicKeyCredentialCreationOptions(
194+
rp,
195+
user,
196+
challenge,
197+
[{"type": "public-key", "alg": -8}, {"type": "public-key", "alg": -7}],
198+
)
199+
result = client.make_credential(options, pin=pin)
200+
attest = result.attestation_object
201+
data = result.client_data
200202
try:
201203
attest.verify(data.hash)
202204
except AttributeError:
@@ -208,6 +210,12 @@ def make_credential(self, pin=None):
208210

209211
return cert
210212

213+
def cred_mgmt(self, pin):
214+
client = self.get_current_fido_client()
215+
token = client.client_pin.get_pin_token(pin)
216+
ctap2 = CTAP2(self.get_current_hid_device())
217+
return CredentialManagement(ctap2, client.client_pin.protocol, token)
218+
211219
def enter_solo_bootloader(self,):
212220
"""
213221
If Nitrokey is configured as Nitrokey hacker or something similar,

0 commit comments

Comments
 (0)