diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index d18f164c..5af59b9a 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -77,32 +77,3 @@ jobs: run: | . venv/bin/activate make check-typing - build-onefile: - name: Build onefile - runs-on: windows-latest - steps: - - name: Checkout repository - uses: actions/checkout@v4 - - name: Create virtual environment - run: | - python -m venv venv - .\venv\Scripts\Activate.ps1 - .\venv\Scripts\pip install pip - .\venv\Scripts\pip install flit - .\venv\Scripts\flit install --symlink - - name: Create Windows version info file - run: | - .\venv\Scripts\Activate.ps1 - create-version-file ` - --outfile .\ci-scripts\windows\pyinstaller\file_version_info.txt ` - --version "$(Get-Content .\pynitrokey\VERSION)" ` - .\ci-scripts\windows\pyinstaller\file_version_info_metadata.yaml - - name: Build onefile - run: | - .\venv\Scripts\Activate.ps1 - pyinstaller ci-scripts/windows/pyinstaller/pynitrokey-onefile.spec - - name: Archive production artifacts - uses: actions/upload-artifact@v4 - with: - name: dist - path: dist\nitropy.exe diff --git a/pynitrokey/cli/nk3/piv.py b/pynitrokey/cli/nk3/piv.py index 03398034..28dd040b 100644 --- a/pynitrokey/cli/nk3/piv.py +++ b/pynitrokey/cli/nk3/piv.py @@ -25,7 +25,7 @@ def piv() -> None: pass -@piv.command() +@piv.command(help="Authenticate with the admin key.") @click.argument( "admin-key", type=click.STRING, @@ -45,7 +45,7 @@ def admin_auth(admin_key: str) -> None: local_print("Authenticated successfully") -@piv.command() +@piv.command(help="Initialize the PIV application.") @click.argument( "admin-key", type=click.STRING, @@ -67,7 +67,7 @@ def init(admin_key: str) -> None: local_print(f"GUID: {guid.hex().upper()}") -@piv.command() +@piv.command(help="Print information about the PIV application.") def info() -> None: device = PivApp() serial_number = device.serial() @@ -94,11 +94,12 @@ def info() -> None: pass -@piv.command() +@piv.command(help="Change the admin key.") @click.option( "--current-admin-key", type=click.STRING, default="010203040506070801020304050607080102030405060708", + help="Current admin key.", ) @click.argument( "new-admin-key", @@ -120,18 +121,20 @@ def change_admin_key(current_admin_key: str, new_admin_key: str) -> None: local_print("Changed key successfully") -@piv.command() +@piv.command(help="Change the PIN.") @click.option( "--current-pin", type=click.STRING, prompt="Enter the PIN", hide_input=True, + help="Current PIN.", ) @click.option( "--new-pin", type=click.STRING, prompt="Enter the PIN", hide_input=True, + help="New PIN.", ) def change_pin(current_pin: str, new_pin: str) -> None: device = PivApp() @@ -139,18 +142,20 @@ def change_pin(current_pin: str, new_pin: str) -> None: local_print("Changed pin successfully") -@piv.command() +@piv.command(help="Change the PUK.") @click.option( "--current-puk", type=click.STRING, prompt="Enter the current PUK", hide_input=True, + help="Current PUK.", ) @click.option( "--new-puk", type=click.STRING, prompt="Enter the new PUK", hide_input=True, + help="New PUK.", ) def change_puk(current_puk: str, new_puk: str) -> None: device = PivApp() @@ -158,18 +163,20 @@ def change_puk(current_puk: str, new_puk: str) -> None: local_print("Changed puk successfully") -@piv.command() +@piv.command(help="Reset the retry counter.") @click.option( "--puk", type=click.STRING, prompt="Enter the PUK", hide_input=True, + help="Current PUK.", ) @click.option( "--new-pin", type=click.STRING, prompt="Enter the new PIN", hide_input=True, + help="New PIN.", ) def reset_retry_counter(puk: str, new_pin: str) -> None: device = PivApp() @@ -177,7 +184,7 @@ def reset_retry_counter(puk: str, new_pin: str) -> None: local_print("Unlocked PIN successfully") -@piv.command() +@piv.command(help="Reset the PIV application.") def factory_reset() -> None: device = PivApp() try: @@ -218,11 +225,12 @@ def factory_reset() -> None: } -@piv.command() +@piv.command(help="Generate a new key and certificate signing request.") @click.option( "--admin-key", type=click.STRING, default="010203040506070801020304050607080102030405060708", + help="Current admin key", ) @click.option( "--key", @@ -252,39 +260,47 @@ def factory_reset() -> None: "93", "94", "95", - ] + ], + case_sensitive=False, ), default="9A", + help="Key slot for operation.", ) @click.option( "--algo", - type=click.Choice(["rsa2048", "nistp256"]), + type=click.Choice(["rsa2048", "nistp256"], case_sensitive=False), default="nistp256", + help="Algorithm for the key.", ) @click.option( "--domain-component", type=click.STRING, multiple=True, + help="Domain component for the certificate signing request.", ) @click.option( "--subject-name", type=click.STRING, multiple=True, + help="Subject name for the certificate signing request.", ) @click.option( "--subject-alt-name-upn", type=click.STRING, + help="Subject alternative name (UPN) for the certificate signing request.", ) @click.option( "--pin", type=click.STRING, prompt="Enter the PIN", hide_input=True, + help="Current PIN.", ) @click.option( - "--out-file", + "--path", type=click.Path(allow_dash=True), default="-", + help="Write certificate signing request to path.", ) def generate_key( admin_key: str, @@ -294,7 +310,7 @@ def generate_key( subject_name: Optional[Sequence[str]], subject_alt_name_upn: Optional[str], pin: str, - out_file: str, + path: str, ) -> None: try: admin_key_bytes = bytearray.fromhex(admin_key) @@ -303,13 +319,14 @@ def generate_key( "Key is expected to be an hexadecimal string", support_hint=False, ) - key_hex = key + key_hex = key.upper() key_ref = int(key_hex, 16) device = PivApp() device.authenticate_admin(admin_key_bytes) device.login(pin) + algo = algo.lower() if algo == "rsa2048": algo_id = b"\x07" signature_algorithm = "sha256_rsa" @@ -509,7 +526,7 @@ def generate_key( } ) - with click.open_file(out_file, mode="wb") as file: + with click.open_file(path, mode="wb") as file: file.write(csr.dump()) cert_info = x509.TbsCertificate( @@ -563,13 +580,18 @@ def generate_key( device.send_receive(0xDB, 0x3F, 0xFF, payload) -@piv.command() +@piv.command(help="Write a certificate to a key slot.") @click.argument( "admin-key", type=click.STRING, default="010203040506070801020304050607080102030405060708", ) -@click.option("--format", type=click.Choice(["DER", "PEM"]), default="PEM") +@click.option( + "--format", + type=click.Choice(["DER", "PEM"], case_sensitive=False), + default="PEM", + help="Format of certificate.", +) @click.option( "--key", type=click.Choice( @@ -598,14 +620,17 @@ def generate_key( "93", "94", "95", - ] + ], + case_sensitive=False, ), default="9A", + help="Key slot for operation.", ) @click.option( "--path", type=click.Path(allow_dash=True), default="-", + help="Write certificate to path.", ) def write_certificate(admin_key: str, format: str, key: str, path: str) -> None: try: @@ -621,6 +646,7 @@ def write_certificate(admin_key: str, format: str, key: str, path: str) -> None: with click.open_file(path, mode="rb") as f: cert_bytes = f.read() + format = format.upper() if format == "DER": cert_serialized = cert_bytes cert = cryptography.x509.load_der_x509_certificate(cert_bytes) @@ -630,7 +656,7 @@ def write_certificate(admin_key: str, format: str, key: str, path: str) -> None: payload = Tlv.build( [ - (0x5C, bytes(bytearray.fromhex(KEY_TO_CERT_OBJ_ID_MAP[key]))), + (0x5C, bytes(bytearray.fromhex(KEY_TO_CERT_OBJ_ID_MAP[key.upper()]))), (0x53, Tlv.build([(0x70, cert_serialized), (0x71, bytes([0]))])), ] ) @@ -638,8 +664,13 @@ def write_certificate(admin_key: str, format: str, key: str, path: str) -> None: device.send_receive(0xDB, 0x3F, 0xFF, payload) -@piv.command() -@click.option("--out-format", type=click.Choice(["DER", "PEM"]), default="PEM") +@piv.command(help="Read a certificate from a key slot.") +@click.option( + "--format", + type=click.Choice(["DER", "PEM"], case_sensitive=False), + default="PEM", + help="Format of certificate.", +) @click.option( "--key", type=click.Choice( @@ -668,24 +699,32 @@ def write_certificate(admin_key: str, format: str, key: str, path: str) -> None: "93", "94", "95", - ] + ], + case_sensitive=False, ), default="9A", + help="Key slot for operation.", ) -@click.option("--path", type=click.Path(allow_dash=True), default="-") -def read_certificate(out_format: str, key: str, path: str) -> None: +@click.option( + "--path", + type=click.Path(allow_dash=True), + default="-", + help="Read certificate from path.", +) +def read_certificate(format: str, key: str, path: str) -> None: device = PivApp() - value = device.cert(bytes(bytearray.fromhex(KEY_TO_CERT_OBJ_ID_MAP[key]))) + value = device.cert(bytes(bytearray.fromhex(KEY_TO_CERT_OBJ_ID_MAP[key.upper()]))) if value is None: print("Certificate not found", file=sys.stderr) return - if out_format == "DER": + format = format.upper() + if format == "DER": cert_serialized = value cryptography.x509.load_der_x509_certificate(value) - elif out_format == "PEM": + elif format == "PEM": cert = cryptography.x509.load_der_x509_certificate(value) cert_serialized = cert.public_bytes(Encoding.PEM)