|
1 | 1 | import datetime
|
2 | 2 | import sys
|
3 |
| -from typing import Optional, Sequence, Tuple, Union |
| 3 | +from typing import Any, Iterable, Optional, Sequence, Tuple, Union |
4 | 4 |
|
5 | 5 | import click
|
6 | 6 | import cryptography
|
@@ -118,6 +118,26 @@ def sign(
|
118 | 118 |
|
119 | 119 | return self._device.sign_p256(data, self._key_reference)
|
120 | 120 |
|
| 121 | + def print_row(values: Iterable[str], widths: Iterable[int]) -> None: |
| 122 | + row = [value.ljust(width) for (value, width) in zip(values, widths)] |
| 123 | + print(*row, sep="\t") |
| 124 | + |
| 125 | + def print_table(headers: Sequence[str], data: Iterable[Sequence[Any]]) -> None: |
| 126 | + widths = [len(header) for header in headers] |
| 127 | + str_data = [] |
| 128 | + for row in data: |
| 129 | + str_row = [] |
| 130 | + for i in range(len(widths)): |
| 131 | + str_value = str(row[i]) |
| 132 | + str_row.append(str_value) |
| 133 | + widths[i] = max(widths[i], len(str_value)) |
| 134 | + str_data.append(str_row) |
| 135 | + |
| 136 | + print_row(headers, widths) |
| 137 | + print_row(["-" * width for width in widths], widths) |
| 138 | + for row in str_data: |
| 139 | + print_row(row, widths) |
| 140 | + |
121 | 141 | @nk3.group()
|
122 | 142 | @click.option(
|
123 | 143 | "--experimental",
|
@@ -180,22 +200,6 @@ def info() -> None:
|
180 | 200 | guid = device.guid()
|
181 | 201 | local_print(f"GUID: {guid.hex().upper()}")
|
182 | 202 |
|
183 |
| - printed_head = False |
184 |
| - for key, slot in KEY_TO_CERT_OBJ_ID_MAP.items(): |
185 |
| - cert = device.cert(bytes(bytearray.fromhex(slot))) |
186 |
| - if cert is not None: |
187 |
| - if not printed_head: |
188 |
| - local_print("Keys:") |
189 |
| - printed_head = True |
190 |
| - parsed_cert = x509.load_der_x509_certificate(cert) |
191 |
| - local_print(f" {key}") |
192 |
| - local_print( |
193 |
| - f" algorithm: {parsed_cert.signature_algorithm_oid._name}" |
194 |
| - ) |
195 |
| - if not printed_head: |
196 |
| - local_print("No certificate found") |
197 |
| - pass |
198 |
| - |
199 | 203 | @piv.command(help="Change the admin key.")
|
200 | 204 | @click.option(
|
201 | 205 | "--current-admin-key",
|
@@ -768,6 +772,30 @@ def read_certificate(format: str, key: str, path: str) -> None:
|
768 | 772 | with click.open_file(path, mode="wb") as f:
|
769 | 773 | f.write(cert_serialized)
|
770 | 774 |
|
| 775 | + @piv.command(help="List certificates.") |
| 776 | + def list_certificates() -> None: |
| 777 | + device = PivApp() |
| 778 | + |
| 779 | + headers = ["Slot", "Algorithm", "Subject"] |
| 780 | + data = [] |
| 781 | + |
| 782 | + for key, slot in KEY_TO_CERT_OBJ_ID_MAP.items(): |
| 783 | + cert = device.cert(bytes(bytearray.fromhex(slot))) |
| 784 | + if cert is not None: |
| 785 | + parsed_cert = x509.load_der_x509_certificate(cert) |
| 786 | + data.append( |
| 787 | + [ |
| 788 | + key, |
| 789 | + parsed_cert.signature_algorithm_oid._name, |
| 790 | + parsed_cert.subject.rfc4514_string(), |
| 791 | + ] |
| 792 | + ) |
| 793 | + |
| 794 | + if data: |
| 795 | + print_table(headers, data) |
| 796 | + else: |
| 797 | + local_print("No certificate found.") |
| 798 | + |
771 | 799 | except ImportError:
|
772 | 800 | from pynitrokey.cli.nk3.pcsc_absent import PCSC_ABSENT
|
773 | 801 |
|
|
0 commit comments