Skip to content

PIV: X.509 key usage option for certificates and CSRs - #164

Merged
framefilter merged 6 commits into
framefilter:mainfrom
episource:feature/certificate-usage
Oct 6, 2026
Merged

framefilter merged 6 commits into
framefilter:mainfrom
episource:feature/certificate-usage

Conversation

@episource

@episource episource commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Adds a key usage option to PIV self-signed certificates and CSRs, in both the GUI (dropdown in the slot's Certificate section) and keyroostctl (--key-usage on piv self-sign and piv request-cert). The selection is written as a X.509 keyUsage extension.

  • Choose any of the X.509 usages, several at once, or undefined, which writes no extension (just like current KeyRoost does unconditionally)
  • Slot default selects the PIV profile for the slot (CLI: default).
  • Warns about key usages incompatible with the slot's key.

episource and others added 3 commits October 4, 2026 20:25
Let the slot's Certificate section request an X.509 keyUsage
extension, so certificates and CSRs can carry the usages the PIV
profiles prescribe instead of never having any extension.

The selection is remembered per device:slot, starts at the PIV
default for the slot, follows the slot key's algorithm (keyEncipherment
and keyAgreement swap, unsupported bits are disabled and dropped) and
"Undefined" keeps today's no-extension output. "Default" is a UI-only
shortcut. keyroostctl passes no key usage and is unchanged.

Co-Authored-By: Claude <noreply@anthropic.com>
Unsupported key usages used to be disabled in the dropdown. Keep them
selectable but flag them in the warning colour, and show a warning
marker next to the dropdown listing what is incompatible with the
slot key's type. A CA or later verifier may still reject such a
certificate, which the tooltip says.

Also rename the "Default" entry to "Slot default".

Co-Authored-By: Claude <noreply@anthropic.com>
piv self-sign and piv request-cert can now request an X.509 keyUsage
extension. Values are the nine usages plus "default" (the PIV
standard's usages for the slot) and "undefined" (no extension, same
as omitting the option). "undefined" must stand alone; "default" may
only accompany exactly the usages that form the default. Invalid
RFC 5280 combinations fail before any PIN is read where the key type
is known. Usages the key type can't back only warn.

Co-Authored-By: Claude <noreply@anthropic.com>
@n0xena

n0xena commented Oct 5, 2026

Copy link
Copy Markdown

according to 5280 section 4.2.1.9 Basic Constraints

Conforming CAs MUST include this extension in all CA certificates that contain public keys used to validate digital signatures on certificates and MUST mark the extension as critical in such certificates.

So if we start to play around with extensions it becomes quite more complicated quite fast.
I'm playing around with certificates for years know and hence read through 5280 and its updates many times about what to include where.
The above quote requires for basicConstraints be present if keyUsage is set to keyCertSign. Also: In such context both extensions should be critical as well.

I do appreciate the work and it makes it simple to play around with tokens even more by introducing x509 - but should keyroost really become some gui certificate manager? If at all I would put this as some form of external like an extension or plugin or even its own application.

The keyUsage extension used to be marked critical whenever it was
written. Make that opt-in: a "Critical" entry in the GUI dropdown and
a `critical` value for keyroostctl --key-usage. It needs at least one
usage and can't be combined with Undefined.

The PIV slot default now includes critical, as the PIV profiles
require it. In the GUI "Slot default" is only shown when both usages
and critical match; in the CLI `default` stands alone or with exactly
those usages plus `critical`.

Co-Authored-By: Claude <noreply@anthropic.com>
@episource

episource commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor Author

To test PIV, support for some extensions is crucial. Key Usage is just a prominent example I've started with, as this is evaluated by my KeePass plugin. Indeed, supporting all PIV-mandated extensions makes the UI more complex. Not all extensions are of practical relevance, though. To keep the UI lean, only a practical subset may be incorporated.

Supporting certificate extensions in the PIV module would not be specific to Key Roost; HID's Crescendo Manager has nicely implemented this as well (screenshots in section New Keys & Self-Signed Certificates).

The "Basic Constraints" extension you've cited is optional for PIV, as ordinary PIV certificates are commonly end-entity certificates. The PIV standard document FIPS201 referenced/required Federal PKI policy therefore marks "Basic Constraints" as optional and requires CA=false if it is present (see e.g. worksheet 6). But using a PIV key as an lightweight/minimal HSM for a CA might be a tempting and valid use case outside the strict PIV certificate profile. But how far KeyRoost wants to support this is a different question. The FIPS201 required strict Non-CA profile is effectively relevant/required for US government applications only.

With several usages selected the caption ran off the pane on a small
window. Let it wrap at the pane edge, keeping room for the warning
marker next to it.

Co-Authored-By: Claude <noreply@anthropic.com>
@n0xena

n0xena commented Oct 5, 2026

Copy link
Copy Markdown

Oh, wasn't aware of PIV specifics when it comes to x509 - but since I started to play around with PIV I somewhat phased out this whole "US" stuff - as I don't care about such over here in europe (and in fact, if at all, would rather focus on europe specifucs if such exists).

@framefilter

framefilter commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Thanks @episource! I do have a couple thoughts, welcome your input:

  1. You have the GUI pre-select "Slot default" while the CLI defaults to no extension. I'd like them to function same/similarly. I think both should default to "Undefined" so a GUI self-sign matches the CLI, unless there's something I'm missing. Are you okay with that or should we go a different direction?
  2. For an Ed25519 key in 9D or the retired slots, "Slot default" sets keyAgreement, which Ed25519 can't do. Should it fall back to no usage there?

We'll add the changelog entry when we merge, and will hold and rebase #165 once #163 and #164 are merged in to avoid causing you a bunch of pain.

Ed25519 can't do key agreement, yet 9D and the retired slots default to
keyAgreement. Intersect the slot default with what the key can back; if
nothing is left the default is undefined (no extension).

The CLI warns when `--key-usage default` degrades this way. The GUI marks
the "Slot default (undefined)" entry and the box with a warning triangle,
and lets Undefined be picked separately from Slot default in that case.

Co-Authored-By: Claude <noreply@anthropic.com>
@episource

Copy link
Copy Markdown
Contributor Author

Thanks for catching the ED25519 issue. I've just relied on the PIV defaults, but missed that there is one specific ECC algorithm not supporting Key Agree. I've fixed this.

Regarding the CLI's default: I'd rather prefer default here too. I did not go that way already, because it would change CLI semantics of existing scripts with the next release. Don't know if you care while keyroost is still in the v0-era.

@framefilter

framefilter commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Thanks @episource! You convinced me: "Slot default" will be the default everywhere.
I’ll make that change once it’s merged in, along with the migration note, so nothing more needed from you here. Regarding changing CLI semantics in the v0.x era, I’m still trying to find the correct balance between letting the user scriptify things, good manual UX, feature parity between CLI and GUI, and having as much consistency as possible between the features. EG if features are similar I don’t want one model to use —disregard when others use —ignore (made up illustrative example). With the breadth of devices keyroost is attempting to support, I’d rather break things now to get them right, and get feedback from supportive early adopters. If I just bandaid everything now and then make massive changes later, that seems like a pretty frustrating thing for an established user base.

@framefilter
framefilter merged commit 58135b6 into framefilter:main Oct 6, 2026
12 checks passed
framefilter added a commit that referenced this pull request Oct 6, 2026
Changelog fragments and README contributor credit for the Swissbit
iShield reads (#163) and X.509 key usage (#164). The v0.13.0 migration
table gains a row for `piv self-sign` / `request-cert`, which now write
the slot's standard key usage when `--key-usage` is omitted, with
`--key-usage undefined` as the way back to the old output.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Skvxkb6eMzytjPDmL6mPYU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants