Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4739f797de | |||
| e0db8e1bab | |||
| 9dc893286a | |||
| d5d126c01e | |||
| bfafb30da3 | |||
| 2b292352a6 | |||
| 0ff0ccd053 | |||
| fd78641514 | |||
| c59bf29a88 | |||
| abd1c7945f |
@@ -17,7 +17,7 @@ jobs:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
python-version: ["3.8", "3.9", "3.10"]
|
||||
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
+7
-13
@@ -18,10 +18,9 @@
|
||||
*/
|
||||
"""
|
||||
|
||||
from enum import IntEnum
|
||||
from .core import NamedIntEnum
|
||||
|
||||
|
||||
class PhyTag(IntEnum):
|
||||
class PhyTag(NamedIntEnum):
|
||||
VIDPID = 0x0
|
||||
LED_GPIO = 0x4
|
||||
LED_BTNESS = 0x5
|
||||
@@ -32,15 +31,13 @@ class PhyTag(IntEnum):
|
||||
ENABLED_USB_ITF = 0xB
|
||||
LED_DRIVER = 0xC
|
||||
|
||||
|
||||
class PhyOpt(IntEnum):
|
||||
class PhyOpt(NamedIntEnum):
|
||||
WCID = 0x1
|
||||
DIMM = 0x2
|
||||
DISABLE_POWER_RESET = 0x4
|
||||
LED_STEADY = 0x8
|
||||
|
||||
|
||||
class PhyCurve(IntEnum):
|
||||
class PhyCurve(NamedIntEnum):
|
||||
SECP256R1 = 0x1
|
||||
SECP384R1 = 0x2
|
||||
SECP521R1 = 0x4
|
||||
@@ -53,15 +50,13 @@ class PhyCurve(IntEnum):
|
||||
CURVE25519 = 0x200
|
||||
CURVE448 = 0x400
|
||||
|
||||
|
||||
class PhyUsbItf(IntEnum):
|
||||
class PhyUsbItf(NamedIntEnum):
|
||||
CCID = 0x1
|
||||
WCID = 0x2
|
||||
HID = 0x4
|
||||
KB = 0x8
|
||||
|
||||
|
||||
class PhyLedDriver(IntEnum):
|
||||
class PhyLedDriver(NamedIntEnum):
|
||||
PICO = 0x1
|
||||
PIMORONI = 0x2
|
||||
WS2812 = 0x3
|
||||
@@ -69,7 +64,6 @@ class PhyLedDriver(IntEnum):
|
||||
NEOPIXEL = 0x5
|
||||
NONE = 0xFF
|
||||
|
||||
|
||||
class PhyData:
|
||||
def __init__(self, **kwargs):
|
||||
self.vidpid = kwargs.get("vidpid")
|
||||
@@ -121,7 +115,7 @@ class PhyData:
|
||||
b = bytearray()
|
||||
if self.vidpid:
|
||||
b += bytes([PhyTag.VIDPID, 4])
|
||||
b += bytes([self.vidpid[1], self.vidpid[0], self.vidpid[3], self.vidpid[2]])
|
||||
b += bytes(self.vidpid[:4])
|
||||
if self.led_gpio is not None:
|
||||
b += bytes([PhyTag.LED_GPIO, 1, self.led_gpio & 0xFF])
|
||||
if self.led_brightness is not None:
|
||||
|
||||
+66
-34
@@ -20,55 +20,53 @@
|
||||
import sys
|
||||
from .APDU import APDUResponse
|
||||
from .SecureChannel import SecureChannel
|
||||
|
||||
try:
|
||||
from smartcard.CardType import AnyCardType
|
||||
from smartcard.CardRequest import CardRequest
|
||||
from smartcard.Exceptions import CardRequestTimeoutException, CardConnectionException
|
||||
except ModuleNotFoundError:
|
||||
print('ERROR: smartcard module not found! Install pyscard package.\nTry with `pip install pyscard`')
|
||||
sys.exit(-1)
|
||||
|
||||
from .RescuePicoKey import RescuePicoKey
|
||||
from .PhyData import PhyData
|
||||
import enum
|
||||
from .core import NamedIntEnum
|
||||
|
||||
class Platform(enum.IntEnum):
|
||||
class Platform(NamedIntEnum):
|
||||
RP2040 = 0
|
||||
RP2350 = 1
|
||||
ESP32 = 2
|
||||
EMULATION = 3
|
||||
|
||||
class Product(enum.IntEnum):
|
||||
class Product(NamedIntEnum):
|
||||
UNKNOWN = 0
|
||||
HSM = 1
|
||||
FIDO = 2
|
||||
OPENPGP = 3
|
||||
|
||||
|
||||
class PicoKey:
|
||||
def __init__(self, slot=-1):
|
||||
def __init__(self, slot=-1, force_rescue=False):
|
||||
self.__sc = None
|
||||
cardtype = AnyCardType()
|
||||
try:
|
||||
# request card insertion
|
||||
readers = None
|
||||
if (slot >= 0):
|
||||
readers = CardRequest().getReaders()
|
||||
if (slot >= len(readers)):
|
||||
raise Exception('slot out of range')
|
||||
readers = [readers[slot]]
|
||||
cardrequest = CardRequest(timeout=1, cardType=cardtype, readers=readers)
|
||||
self.__card = cardrequest.waitforcard().connection
|
||||
|
||||
# connect to the card and perform a few transmits
|
||||
self.__card.connect()
|
||||
|
||||
except CardRequestTimeoutException:
|
||||
if (force_rescue):
|
||||
try:
|
||||
self.__card = RescuePicoKey()
|
||||
except Exception:
|
||||
raise Exception('time-out: no card inserted')
|
||||
else:
|
||||
from smartcard.CardType import AnyCardType
|
||||
from smartcard.CardRequest import CardRequest
|
||||
cardtype = AnyCardType()
|
||||
try:
|
||||
# request card insertion
|
||||
readers = None
|
||||
if (slot >= 0):
|
||||
readers = CardRequest().getReaders()
|
||||
if (slot >= len(readers)):
|
||||
raise Exception('slot out of range')
|
||||
readers = [readers[slot]]
|
||||
cardrequest = CardRequest(timeout=1, cardType=cardtype, readers=readers)
|
||||
self.__card = cardrequest.waitforcard().connection
|
||||
|
||||
# connect to the card and perform a few transmits
|
||||
self.__card.connect()
|
||||
|
||||
except Exception:
|
||||
try:
|
||||
self.__card = RescuePicoKey()
|
||||
except Exception:
|
||||
raise Exception('time-out: no card inserted')
|
||||
resp, sw1, sw2 = self.select_applet(rescue=True)
|
||||
try:
|
||||
resp, sw1, sw2 = self.select_applet(rescue=True)
|
||||
@@ -109,7 +107,7 @@ class PicoKey:
|
||||
|
||||
try:
|
||||
response, sw1, sw2 = self.__card.transmit(apdu)
|
||||
except CardConnectionException:
|
||||
except Exception:
|
||||
self.__card.reconnect()
|
||||
response, sw1, sw2 = self.__card.transmit(apdu)
|
||||
|
||||
@@ -145,7 +143,7 @@ class PicoKey:
|
||||
|
||||
try:
|
||||
response, sw1, sw2 = self.__card.transmit(apdu)
|
||||
except CardConnectionException:
|
||||
except Exception:
|
||||
self.__card.reconnect()
|
||||
response, sw1, sw2 = self.__card.transmit(apdu)
|
||||
|
||||
@@ -165,7 +163,41 @@ class PicoKey:
|
||||
|
||||
def phy(self, data=None):
|
||||
if (data is None):
|
||||
resp, sw = self.send(0x1E, cla=0x80, p1=0x01, ne=256)
|
||||
return PhyData.parse(resp)
|
||||
try:
|
||||
resp, sw = self.send(0x1E, cla=0x80, p1=0x00, ne=256)
|
||||
return PhyData.parse(resp)
|
||||
except APDUResponse:
|
||||
pass
|
||||
return None
|
||||
else:
|
||||
self.send(0x1C, cla=0x80, p1=0x01, data=data)
|
||||
|
||||
def flash_info(self):
|
||||
try:
|
||||
resp, sw = self.send(0x1E, cla=0x80, p1=0x02)
|
||||
free = int.from_bytes(resp[0:4], 'big')
|
||||
used = int.from_bytes(resp[4:8], 'big')
|
||||
total = int.from_bytes(resp[8:12], 'big')
|
||||
nfiles = int.from_bytes(resp[12:16], 'big')
|
||||
size = int.from_bytes(resp[16:20], 'big')
|
||||
except Exception:
|
||||
free = used = total = nfiles = size = 0
|
||||
return {
|
||||
'free': free,
|
||||
'used': used,
|
||||
'total': total,
|
||||
'nfiles': nfiles,
|
||||
'size': size
|
||||
}
|
||||
|
||||
def secure_info(self):
|
||||
resp, sw = self.send(0x1E, cla=0x80, p1=0x03)
|
||||
return {
|
||||
'enabled': resp[0] != 0,
|
||||
'locked': resp[1] != 0,
|
||||
'boot_key': resp[2]
|
||||
}
|
||||
|
||||
def secure_boot(self, bootkey_index=0, lock=False):
|
||||
data = bytes([bootkey_index & 0xFF, 1 if lock else 0])
|
||||
self.send(0x1C, cla=0x80, p1=0x02, data=data)
|
||||
|
||||
@@ -5,3 +5,4 @@ from .APDU import APDUResponse
|
||||
from .SWCodes import SWCodes
|
||||
from .RescuePicoKey import RescuePicoKey
|
||||
from .PhyData import PhyData, PhyCurve, PhyUsbItf, PhyLedDriver, PhyOpt
|
||||
from .core import enums
|
||||
|
||||
+1
-1
@@ -18,4 +18,4 @@
|
||||
*/
|
||||
"""
|
||||
|
||||
__version__ = "1.0"
|
||||
__version__ = "1.1.2"
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
from .enums import *
|
||||
@@ -0,0 +1,5 @@
|
||||
import enum
|
||||
|
||||
class NamedIntEnum(enum.IntEnum):
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -36,7 +36,7 @@ INSTALL_REQUIRES = [
|
||||
'cryptography>=3.3',
|
||||
'pyusb',
|
||||
'pycvc',
|
||||
'pyscard<=2.2.2',
|
||||
'pyscard>=2.3.1',
|
||||
]
|
||||
|
||||
if sys.platform.startswith('win32'):
|
||||
@@ -50,7 +50,7 @@ except(IOError, ImportError):
|
||||
|
||||
setup(
|
||||
name='pypicokey',
|
||||
packages=['picokey'],
|
||||
packages=['picokey','picokey/core'],
|
||||
version=version,
|
||||
description='PicoKey for Python',
|
||||
license='AGPL',
|
||||
|
||||
Reference in New Issue
Block a user