keylet 0.1.0__tar.gz → 0.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
keylet-0.2.0/PKG-INFO ADDED
@@ -0,0 +1,76 @@
1
+ Metadata-Version: 2.3
2
+ Name: keylet
3
+ Version: 0.2.0
4
+ Summary: Client application for Tillitis TKey hardware ML-DSA signer
5
+ Author: Jussi Kukkonen
6
+ Author-email: Jussi Kukkonen <jkukkonen@google.com>
7
+ Requires-Dist: cryptography>=48.0.0
8
+ Requires-Dist: pyserial>=3.5
9
+ Requires-Python: >=3.10
10
+ Project-URL: Issues, https://github.com/jku/keylet/issues
11
+ Project-URL: Source, https://github.com/jku/keylet
12
+ Project-URL: Documentation, https://jku.github.io/keylet/
13
+ Description-Content-Type: text/markdown
14
+
15
+ # keylet -- Client library for Tillitis TKey
16
+
17
+ [![keylet on GitHub](https://img.shields.io/badge/GitHub-jku%2Fkeylet-blue?logo=github)](https://github.com/jku/keylet) [![keylet on PyPI](https://img.shields.io/pypi/v/keylet.svg)](https://pypi.org/project/keylet/) [![keylet documentation](https://img.shields.io/badge/Documentation-blue)](https://jku.github.io/keylet/)
18
+
19
+ Keylet is a Python client library and CLI tool for the [Tillitis TKey](https://www.tillitis.se/products/tkey/) security token, and implements a ML-DSA / Ed25519 signer application for TKey.
20
+
21
+ ## Installation
22
+
23
+ ```bash
24
+ pip install keylet
25
+ ```
26
+
27
+ ## CLI Usage
28
+
29
+ The package installs a `keylet` command-line tool forsigning and verification. This is primarily a test/demo application for the library.
30
+
31
+ ```bash
32
+ # Sign without a passphrase, then verify
33
+ $ keylet sign README.md
34
+ $ keylet verify README.md
35
+
36
+ # Get public key, sign with a passphrase, and verify using the saved public key
37
+ $ keylet --passphrase hunter2 pubkey --output pub.key
38
+ $ keylet --passphrase hunter2 sign README.md
39
+ $ keylet verify --pubkey pub.key README.md
40
+
41
+ # When using keylet long-term, remember to specify device app digest (keylet default
42
+ # app version may change, but you will need a specific application to keep using the
43
+ # same key)
44
+ $ keylet --passphrase hunter2 --digest 186bcf6 sign README.md
45
+
46
+ ```
47
+
48
+ ## Library Usage
49
+
50
+ ```python
51
+ from keylet import TKeySign, SignApp
52
+
53
+ # Load the default embedded ML-DSA signer
54
+ app = SignApp.load_mldsa()
55
+ digest = app.digest
56
+
57
+ # Initialize the signer with a passphrase
58
+ with TKeySign(app=app, secret="hunter2") as signer:
59
+ # Sign a payload
60
+ signature = signer.sign(b"my payload")
61
+ ```
62
+
63
+ In long-term use, the device app digest should be used to ensure the same application
64
+ is always used for a specific key:
65
+
66
+ ```python
67
+ # Load application with a digest stored earlier
68
+ app = SignApp.load_mldsa(digest=digest)
69
+
70
+ # Initialize the signer with a passphrase
71
+ with TKeySign(app=app, secret="hunter2") as signer:
72
+ # Sign a payload
73
+ signature = signer.sign(b"my payload")
74
+ ```
75
+
76
+ See the [API Reference](https://jku.github.io/keylet/api/) for more details.
keylet-0.2.0/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # keylet -- Client library for Tillitis TKey
2
+
3
+ [![keylet on GitHub](https://img.shields.io/badge/GitHub-jku%2Fkeylet-blue?logo=github)](https://github.com/jku/keylet) [![keylet on PyPI](https://img.shields.io/pypi/v/keylet.svg)](https://pypi.org/project/keylet/) [![keylet documentation](https://img.shields.io/badge/Documentation-blue)](https://jku.github.io/keylet/)
4
+
5
+ Keylet is a Python client library and CLI tool for the [Tillitis TKey](https://www.tillitis.se/products/tkey/) security token, and implements a ML-DSA / Ed25519 signer application for TKey.
6
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ pip install keylet
11
+ ```
12
+
13
+ ## CLI Usage
14
+
15
+ The package installs a `keylet` command-line tool forsigning and verification. This is primarily a test/demo application for the library.
16
+
17
+ ```bash
18
+ # Sign without a passphrase, then verify
19
+ $ keylet sign README.md
20
+ $ keylet verify README.md
21
+
22
+ # Get public key, sign with a passphrase, and verify using the saved public key
23
+ $ keylet --passphrase hunter2 pubkey --output pub.key
24
+ $ keylet --passphrase hunter2 sign README.md
25
+ $ keylet verify --pubkey pub.key README.md
26
+
27
+ # When using keylet long-term, remember to specify device app digest (keylet default
28
+ # app version may change, but you will need a specific application to keep using the
29
+ # same key)
30
+ $ keylet --passphrase hunter2 --digest 186bcf6 sign README.md
31
+
32
+ ```
33
+
34
+ ## Library Usage
35
+
36
+ ```python
37
+ from keylet import TKeySign, SignApp
38
+
39
+ # Load the default embedded ML-DSA signer
40
+ app = SignApp.load_mldsa()
41
+ digest = app.digest
42
+
43
+ # Initialize the signer with a passphrase
44
+ with TKeySign(app=app, secret="hunter2") as signer:
45
+ # Sign a payload
46
+ signature = signer.sign(b"my payload")
47
+ ```
48
+
49
+ In long-term use, the device app digest should be used to ensure the same application
50
+ is always used for a specific key:
51
+
52
+ ```python
53
+ # Load application with a digest stored earlier
54
+ app = SignApp.load_mldsa(digest=digest)
55
+
56
+ # Initialize the signer with a passphrase
57
+ with TKeySign(app=app, secret="hunter2") as signer:
58
+ # Sign a payload
59
+ signature = signer.sign(b"my payload")
60
+ ```
61
+
62
+ See the [API Reference](https://jku.github.io/keylet/api/) for more details.
@@ -0,0 +1,85 @@
1
+ [project]
2
+ name = "keylet"
3
+ version = "0.2.0"
4
+ description = "Client application for Tillitis TKey hardware ML-DSA signer"
5
+ readme = "README.md"
6
+ authors = [
7
+ { name = "Jussi Kukkonen", email = "jkukkonen@google.com" }
8
+ ]
9
+ requires-python = ">=3.10"
10
+ dependencies = [
11
+ "cryptography>=48.0.0",
12
+ "pyserial>=3.5",
13
+ ]
14
+
15
+ [project.scripts]
16
+ keylet = "keylet.bin.cli:main"
17
+
18
+ [project.urls]
19
+ Issues = "https://github.com/jku/keylet/issues"
20
+ Source = "https://github.com/jku/keylet"
21
+ Documentation = "https://jku.github.io/keylet/"
22
+
23
+ [dependency-groups]
24
+ dev = [
25
+ "mypy",
26
+ "pytest",
27
+ "ruff",
28
+ "zizmor",
29
+ "zensical",
30
+ "mkdocstrings[python]",
31
+ ]
32
+
33
+ [build-system]
34
+ requires = ["uv_build>=0.11.23,<0.12.0"]
35
+ build-backend = "uv_build"
36
+
37
+ [tool.ruff]
38
+ line-length = 88
39
+
40
+ [tool.ruff.lint]
41
+ select = [
42
+ "E", # pycodestyle errors
43
+ "W", # pycodestyle warnings
44
+ "F", # pyflakes
45
+ "I", # isort
46
+ "B", # flake8-bugbear
47
+ "C4", # flake8-comprehensions
48
+ "UP", # pyupgrade
49
+ "RUF", # Ruff-specific rules
50
+ "S", # flake8-bandit (Security)
51
+ "SIM", # flake8-simplify
52
+ "PT", # flake8-pytest-style
53
+ "PTH", # flake8-use-pathlib
54
+ "TID", # flake8-tidy-imports
55
+ "T20", # flake8-print
56
+ "ARG", # flake8-unused-arguments
57
+ "SIM", # flake8-simplify
58
+ ]
59
+ ignore = []
60
+
61
+ [tool.ruff.lint.per-file-ignores]
62
+ "src/keylet/bin/cli.py" = ["T201"]
63
+ "tests/**" = [
64
+ "S101", # Use of assert
65
+ "S105", # Possible hardcoded password
66
+ "PT009", # Use regular assert instead of unittest assertEqual
67
+ "PT027", # Use pytest.raises instead of unittest assertRaises
68
+ "ARG002", # Unused method argument (common with mocks)
69
+ ]
70
+
71
+
72
+
73
+ [tool.mypy]
74
+ python_version = "3.10"
75
+ strict = true
76
+ warn_unreachable = true
77
+
78
+ [[tool.mypy.overrides]]
79
+ module = "serial.*"
80
+ ignore_missing_imports = true
81
+
82
+ [tool.pytest.ini_options]
83
+ markers = [
84
+ "device: tests that require a physical TKey device",
85
+ ]
@@ -6,22 +6,22 @@ import sys
6
6
  from pathlib import Path
7
7
 
8
8
  from cryptography.exceptions import InvalidSignature
9
+ from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PublicKey
9
10
  from cryptography.hazmat.primitives.asymmetric.mldsa import MLDSA44PublicKey
10
11
 
11
12
  from keylet.tkey import TKeyNotFoundError
12
13
  from keylet.tkey_sign import SignApp, TKeySign
13
14
 
14
15
 
15
- def get_signer(device: str | None, passphrase: str | None) -> TKeySign:
16
- """Helper to initialize TKeySign with the default device signer binary."""
17
- app = SignApp.load_mldsa()
18
- return TKeySign(app, device=device, secret=passphrase)
19
-
20
-
21
16
  def cmd_pubkey(args: argparse.Namespace) -> int:
22
17
  try:
23
- with get_signer(args.device, args.passphrase) as signer:
18
+ if args.type == "ed25519":
19
+ app = SignApp.load_ed25519(digest=args.digest)
20
+ else:
21
+ app = SignApp.load_mldsa(digest=args.digest)
22
+ with TKeySign(app, secret=args.passphrase) as signer:
24
23
  pubkey = signer.get_pubkey()
24
+ print(f"Using device app with digest {app.digest[:7]}.")
25
25
  if args.output:
26
26
  Path(args.output).write_bytes(pubkey)
27
27
  print(f"Public key written to {args.output}")
@@ -41,7 +41,12 @@ def cmd_sign(args: argparse.Namespace) -> int:
41
41
 
42
42
  try:
43
43
  data = file_path.read_bytes()
44
- with get_signer(args.device, args.passphrase) as signer:
44
+ if args.type == "ed25519":
45
+ app = SignApp.load_ed25519(digest=args.digest)
46
+ else:
47
+ app = SignApp.load_mldsa(digest=args.digest)
48
+ with TKeySign(app, secret=args.passphrase) as signer:
49
+ print(f"Using device app with digest {app.digest[:7]}.")
45
50
  print("Please touch the TKey device when it flashes to sign...")
46
51
  signature = signer.sign(data)
47
52
 
@@ -77,13 +82,22 @@ def cmd_verify(args: argparse.Namespace) -> int:
77
82
  if args.pubkey:
78
83
  pubkey_bytes = Path(args.pubkey).read_bytes()
79
84
  else:
80
- print("Retrieving public key from device...")
81
- with get_signer(args.device, args.passphrase) as signer:
85
+ if args.type == "ed25519":
86
+ app = SignApp.load_ed25519(digest=args.digest)
87
+ else:
88
+ app = SignApp.load_mldsa(digest=args.digest)
89
+ with TKeySign(app, secret=args.passphrase) as signer:
90
+ print(f"Using device app with digest {app.digest[:7]}.")
91
+ print("Retrieving public key from device...")
82
92
  pubkey_bytes = signer.get_pubkey()
83
93
 
84
94
  # Verify signature using cryptography library
85
- pubkey = MLDSA44PublicKey.from_public_bytes(pubkey_bytes)
86
- pubkey.verify(sig_bytes, file_bytes)
95
+ if args.type == "ed25519":
96
+ ed_pubkey = Ed25519PublicKey.from_public_bytes(pubkey_bytes)
97
+ ed_pubkey.verify(sig_bytes, file_bytes)
98
+ else:
99
+ ml_pubkey = MLDSA44PublicKey.from_public_bytes(pubkey_bytes)
100
+ ml_pubkey.verify(sig_bytes, file_bytes)
87
101
  print("Verification successful!")
88
102
  return 0
89
103
  except InvalidSignature:
@@ -98,10 +112,15 @@ def main() -> None:
98
112
  parser = argparse.ArgumentParser(
99
113
  description="Tillitis TKey Keylet CLI testing tool"
100
114
  )
115
+ parser.add_argument("--digest", help="The digest of the device application to use")
116
+ parser.add_argument("--passphrase", help="User Supplied Secret (passphrase)")
101
117
  parser.add_argument(
102
- "--device", help="Serial port of the TKey device (e.g. /dev/ttyACM0)"
118
+ "-t",
119
+ "--type",
120
+ choices=["ml-dsa", "ed25519"],
121
+ default="ml-dsa",
122
+ help="Signer type (default: %(default)s)",
103
123
  )
104
- parser.add_argument("--passphrase", help="User Supplied Secret (passphrase)")
105
124
 
106
125
  subparsers = parser.add_subparsers(dest="command", required=True)
107
126
 
@@ -17,11 +17,15 @@ logger = logging.getLogger(__name__)
17
17
  MU_SIZE = (64).to_bytes(4, byteorder="little")
18
18
 
19
19
 
20
- # Static registry of all signer binaries packaged in keylet.resources.
21
- # First binary in the list is the default binary.
22
- # Format: (filename, version, name)
23
- _EMBEDDED_MLDSA_BINS: list[tuple[str, int, tuple[str, str]]] = [
24
- ("pqsigner_v3.bin", 3, ("tk1", "pqsn")),
20
+ # Static registry of signer binaries
21
+ # First binary in each list is the default binary.
22
+ # Format: (filename, version)
23
+ _EMBEDDED_MLDSA_BINS: list[tuple[str, int]] = [
24
+ ("pqsigner_v3.bin", 3),
25
+ ]
26
+
27
+ _EMBEDDED_ED25519_BINS: list[tuple[str, int]] = [
28
+ ("ed25519signer_v3.bin", 3),
25
29
  ]
26
30
 
27
31
 
@@ -32,17 +36,16 @@ class SignApp:
32
36
  Attributes:
33
37
  binary: The raw bytes of the device application binary.
34
38
  version: The version number of the device application.
35
- name: A tuple representing the expected firmware name and app name
36
- on the device (defaults to `("tk1", "pqsn")`).
37
- sig_size: The size of the generated signature in bytes (defaults to 2420).
38
- key_size: The size of the public key in bytes (defaults to 1312).
39
+ name: Device application name tuple.
40
+ sig_size: The size of the generated signature in bytes.
41
+ key_size: The size of the public key in bytes.
39
42
  """
40
43
 
41
44
  binary: bytes
42
45
  version: int
43
- name: tuple[str, str] = ("tk1", "pqsn")
44
- sig_size: int = 2420
45
- key_size: int = 1312
46
+ name: tuple[str, str]
47
+ sig_size: int
48
+ key_size: int
46
49
 
47
50
  @property
48
51
  def digest(self) -> str:
@@ -50,34 +53,14 @@ class SignApp:
50
53
  return hashlib.blake2s(self.binary, digest_size=32).hexdigest()
51
54
 
52
55
  @classmethod
53
- def load_mldsa(
54
- cls, version: int | None = None, digest: str | None = None
55
- ) -> SignApp:
56
- """Load a ML-DSA signer application from package resources.
57
-
58
- If a digest (or prefix) is provided, it returns the binary matching the
59
- digest. If a version is provided, it filters by version. If neither is
60
- provided, current default binary is loaded.
61
-
62
- TKey key derivation depends on the application binary, so users who want a
63
- specific key must provide the binary digest.
64
-
65
- Args:
66
- version: The version of the signer application to load.
67
- digest: A BLAKE2s-256 hex digest (or prefix) of the target binary.
68
-
69
- Returns:
70
- An instance of SignApp configured with the loaded binary.
71
-
72
- Raises:
73
- ValueError: If no binary matches the criteria, or if the search
74
- is ambiguous (matches multiple binaries).
75
- """
56
+ def _find_binary(
57
+ cls, version: int | None, digest: str | None, bins: list[tuple[str, int]]
58
+ ) -> tuple[bytes, int]:
76
59
  resources_dir = importlib.resources.files("keylet.resources")
77
60
  matches = []
78
61
 
79
62
  # Scan registered binaries
80
- for filename, file_ver, name in _EMBEDDED_MLDSA_BINS:
63
+ for filename, file_ver in bins:
81
64
  # Filter by version if requested
82
65
  if version is not None and file_ver != version:
83
66
  continue
@@ -89,7 +72,7 @@ class SignApp:
89
72
  if digest is not None and not file_digest.startswith(digest.lower()):
90
73
  continue
91
74
 
92
- matches.append((binary, file_ver, name, filename))
75
+ matches.append((binary, file_ver))
93
76
 
94
77
  if digest is None and version is None:
95
78
  # First binary is the default one
@@ -106,8 +89,62 @@ class SignApp:
106
89
  f"digest={digest}."
107
90
  )
108
91
 
109
- matched_binary, matched_version, matched_name, _ = matches[0]
110
- return cls(matched_binary, matched_version, matched_name)
92
+ return matches[0]
93
+
94
+ @classmethod
95
+ def load_mldsa(
96
+ cls, version: int | None = None, digest: str | None = None
97
+ ) -> SignApp:
98
+ """Load a ML-DSA signer application from package resources.
99
+
100
+ If a digest (or prefix) is provided, it returns the binary matching the
101
+ digest. If a version is provided, it filters by version. If neither is
102
+ provided, current default binary is loaded.
103
+
104
+ TKey key derivation depends on the application binary, so users who want a
105
+ specific key must provide the binary digest.
106
+
107
+ Args:
108
+ version: The version of the signer application to load.
109
+ digest: A BLAKE2s-256 hex digest (or prefix) of the target binary.
110
+
111
+ Returns:
112
+ An instance of SignApp configured with the loaded binary.
113
+
114
+ Raises:
115
+ ValueError: If no binary matches the criteria, or if the search
116
+ is ambiguous (matches multiple binaries).
117
+ """
118
+
119
+ binary, version = cls._find_binary(version, digest, _EMBEDDED_MLDSA_BINS)
120
+ return cls(binary, version, ("tk1", "pqsn"), 2420, 1312)
121
+
122
+ @classmethod
123
+ def load_ed25519(
124
+ cls, version: int | None = None, digest: str | None = None
125
+ ) -> SignApp:
126
+ """Load a Ed25519 signer application from package resources.
127
+
128
+ If a digest (or prefix) is provided, it returns the binary matching the
129
+ digest. If a version is provided, it filters by version. If neither is
130
+ provided, current default binary is loaded.
131
+
132
+ TKey key derivation depends on the application binary, so users who want a
133
+ specific key must provide the binary digest.
134
+
135
+ Args:
136
+ version: The version of the signer application to load.
137
+ digest: A BLAKE2s-256 hex digest (or prefix) of the target binary.
138
+
139
+ Returns:
140
+ An instance of SignApp configured with the loaded binary.
141
+
142
+ Raises:
143
+ ValueError: If no binary matches the criteria, or if the search
144
+ is ambiguous (matches multiple binaries).
145
+ """
146
+ binary, version = cls._find_binary(version, digest, _EMBEDDED_ED25519_BINS)
147
+ return cls(binary, version, ("tk1", "sign"), 64, 32)
111
148
 
112
149
 
113
150
  class SignRsp:
@@ -135,9 +172,10 @@ class SignCmd:
135
172
  class TKeySign(TKey):
136
173
  """Client for communicating with the TKey signer application.
137
174
 
138
- This class extends the base TKey client to implement public key retrieval and
139
- signing as defined in the
140
- [tkey-pq-device-signer protocol](https://github.com/tillitis/tkey-pq-device-signer).
175
+ This class implements public key retrieval and signing as defined in the
176
+ [tkey-pq-device-signer protocol](https://github.com/tillitis/tkey-pq-device-signer)
177
+ but is also compatible with the
178
+ [tkey-device-signer protocol](https://github.com/tillitis/tkey-device-signer).
141
179
  """
142
180
 
143
181
  def __init__(
@@ -168,6 +206,7 @@ class TKeySign(TKey):
168
206
  super().__init__(device)
169
207
  self.key_size = app.key_size
170
208
  self.sig_size = app.sig_size
209
+ self.name = app.name
171
210
 
172
211
  try:
173
212
  if not self.load_app(app.binary, secret):
@@ -215,10 +254,12 @@ class TKeySign(TKey):
215
254
  return bytes(pubkey)
216
255
 
217
256
  def sign(self, message: bytes, pub_key: bytes | None = None) -> bytes:
218
- """Sign a message using ML-DSA.
257
+ """Sign a payload.
219
258
 
220
- Computes the FIPS 204 external mu using the message and public key,
221
- sends it to the device, and retrieves the signature.
259
+ Sends payload to device and retrieves the signature.
260
+
261
+ For ML-DSA, the FIPS 204 external mu is computed using the message
262
+ and public key: the mu is sent to device instead of payload.
222
263
 
223
264
  Note:
224
265
  This method blocks and waits (up to 60 seconds) for the user to touch
@@ -226,8 +267,8 @@ class TKeySign(TKey):
226
267
 
227
268
  Args:
228
269
  message: The raw bytes of the message/payload to sign.
229
- pub_key: The public key bytes used to compute the external mu.
230
- If None, the public key is retrieved from the device.
270
+ pub_key: The public key bytes (only needed for ML-DSA). If not provided,
271
+ key is retrieved from device.
231
272
 
232
273
  Returns:
233
274
  The generated signature as raw bytes.
@@ -237,18 +278,30 @@ class TKeySign(TKey):
237
278
  TKeyIOError: If writing or reading from the serial port fails.
238
279
  TKeyProtocolError: If there is a framing or protocol mismatch.
239
280
  """
240
- if pub_key is None:
241
- pub_key = self.get_pubkey()
242
-
243
- # Compute FIPS 204 external mu
244
- tr = hashlib.shake_256(pub_key).digest(64)
245
- mu = hashlib.shake_256(tr + b"\x00\x00" + message).digest(64)
246
-
247
- # Set size: in our case mu is always 64 bytes
248
- self.send(SignCmd.SET_SIZE, MU_SIZE)
249
-
250
- # Load data: mu fits in single frame
251
- self.send(SignCmd.LOAD_DATA, mu)
281
+ # pqsn = ML-DSA signer, pqnt = no-touch ML-DSA test signer
282
+ if self.name in [("tk1", "pqsn"), ("tk1", "pqnt")]:
283
+ # Compute FIPS 204 external mu
284
+ if pub_key is None:
285
+ pub_key = self.get_pubkey()
286
+ tr = hashlib.shake_256(pub_key).digest(64)
287
+ payload = hashlib.shake_256(tr + b"\x00\x00" + message).digest(64)
288
+ else:
289
+ payload = message
290
+
291
+ # Set size
292
+ if len(payload) > 4096:
293
+ raise ValueError(f"Payload too large {len(payload)} > 4096]")
294
+ self.send(SignCmd.SET_SIZE, len(payload).to_bytes(4, byteorder="little"))
295
+
296
+ # Load data in chunks
297
+ chunk_size = 127
298
+ offset = 0
299
+ while offset < len(payload):
300
+ chunk = payload[offset : offset + chunk_size]
301
+ rx = self.send(SignCmd.LOAD_DATA, chunk)
302
+ if rx[2] != 0:
303
+ raise TKeyError(f"LoadData chunk NOK status: {rx[2]}")
304
+ offset += chunk_size
252
305
 
253
306
  # Trigger signing (blocks waiting for touch) and read first frame
254
307
  rx = self.send(SignCmd.GET_SIG, timeout=60)
keylet-0.1.0/PKG-INFO DELETED
@@ -1,15 +0,0 @@
1
- Metadata-Version: 2.3
2
- Name: keylet
3
- Version: 0.1.0
4
- Summary: Client application Tillitis TKey ML-DSA signer
5
- Author: Jussi Kukkonen
6
- Author-email: Jussi Kukkonen <jkukkonen@google.com>
7
- Requires-Dist: cryptography>=48.0.0
8
- Requires-Dist: pyserial>=3.5
9
- Requires-Python: >=3.10
10
- Description-Content-Type: text/markdown
11
-
12
- ## Keylet -- Client library for Tillitis TKey
13
-
14
- This library provides a client implementation for the Tillitis TKey, and specifically
15
- the [tkey-pq-device-signer](https://github.com/tillitis/tkey-pq-device-signer) application.
keylet-0.1.0/README.md DELETED
@@ -1,4 +0,0 @@
1
- ## Keylet -- Client library for Tillitis TKey
2
-
3
- This library provides a client implementation for the Tillitis TKey, and specifically
4
- the [tkey-pq-device-signer](https://github.com/tillitis/tkey-pq-device-signer) application.
@@ -1,64 +0,0 @@
1
- [project]
2
- name = "keylet"
3
- version = "0.1.0"
4
- description = "Client application Tillitis TKey ML-DSA signer"
5
- readme = "README.md"
6
- authors = [
7
- { name = "Jussi Kukkonen", email = "jkukkonen@google.com" }
8
- ]
9
- requires-python = ">=3.10"
10
- dependencies = [
11
- "cryptography>=48.0.0",
12
- "pyserial>=3.5",
13
- ]
14
-
15
- [project.scripts]
16
- keylet = "keylet.bin.cli:main"
17
-
18
-
19
- [dependency-groups]
20
- dev = [
21
- "mypy>=1.10.0",
22
- "pytest>=8.0.0",
23
- "pytest-cov>=4.1.0",
24
- "ruff>=0.3.0",
25
- "types-pyserial>=3.5.0.20240310",
26
- "zizmor>=0.8.0",
27
- "zensical>=0.0.46",
28
- "mkdocstrings[python]>=1.0.4",
29
- ]
30
-
31
- [build-system]
32
- requires = ["uv_build>=0.11.23,<0.12.0"]
33
- build-backend = "uv_build"
34
-
35
- [tool.ruff]
36
- line-length = 88
37
- target-version = "py310"
38
-
39
- [tool.ruff.lint]
40
- select = [
41
- "E", # pycodestyle errors
42
- "W", # pycodestyle warnings
43
- "F", # pyflakes
44
- "I", # isort
45
- "B", # flake8-bugbear
46
- "C4", # flake8-comprehensions
47
- "UP", # pyupgrade
48
- "RUF", # Ruff-specific rules
49
- ]
50
- ignore = []
51
-
52
- [tool.mypy]
53
- python_version = "3.10"
54
- strict = true
55
- warn_unreachable = true
56
-
57
- [[tool.mypy.overrides]]
58
- module = "serial.*"
59
- ignore_missing_imports = true
60
-
61
- [tool.pytest.ini_options]
62
- markers = [
63
- "device: tests requiring a physical TKey device running a no-touch binary",
64
- ]
File without changes
File without changes
File without changes