Metadata-Version: 2.4
Name: uv-pkcs11
Version: 0.12.15
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: 3.15
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Rust
Classifier: Topic :: Software Development :: Quality Assurance
Classifier: Topic :: Software Development :: Testing
Classifier: Topic :: Software Development :: Libraries
License-File: LICENSE-APACHE
License-File: LICENSE-MIT
Summary: uv with PKCS#11 client-certificate (mTLS) support. Unofficial fork of astral-sh/uv.
Keywords: uv,requirements,packaging,pkcs11,mtls
Home-Page: https://github.com/dtrodrigues/uv-pkcs11
Author-email: Dustin Rodrigues <dust.rod@gmail.com>
License-Expression: MIT OR Apache-2.0
Requires-Python: >=3.8
Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
Project-URL: Documentation, https://github.com/dtrodrigues/uv-pkcs11/blob/pkcs11/README_PKCS11.md
Project-URL: Homepage, https://github.com/dtrodrigues/uv-pkcs11
Project-URL: Repository, https://github.com/dtrodrigues/uv-pkcs11
Project-URL: Upstream, https://github.com/astral-sh/uv
Project-URL: Upstream changelog, https://github.com/astral-sh/uv/blob/main/CHANGELOG.md

# uv-pkcs11

[![PyPI](https://img.shields.io/pypi/v/uv-pkcs11.svg)](https://pypi.org/project/uv-pkcs11/)

**An unofficial fork of [uv](https://github.com/astral-sh/uv)** — the
extremely fast Python package and project manager — with PKCS#11
client-certificate (mTLS) support, so uv can authenticate to package indexes
with keys held in PKCS#11 providers that never expose the private key.

The fork never logs in to a token, so it works with providers whose
certificate and key are usable **without a PIN**: software HSMs, network HSMs
unlocked out of band, and p11-kit-proxied providers configured for loginless
use. Ordinary PIN-protected smart cards and tokens that require `C_Login`
before private-key use are **not supported**. The PKCS#11 support itself is
new (beta): tested end to end against SoftHSM, with limited real-world
provider mileage so far.

This project is not affiliated with or endorsed by Astral. The fork lives at
[github.com/dtrodrigues/uv-pkcs11](https://github.com/dtrodrigues/uv-pkcs11)
and is published on PyPI as
[uv-pkcs11](https://pypi.org/project/uv-pkcs11/); for everything except the
PKCS#11 additions, see the [upstream
documentation](https://docs.astral.sh/uv).

## Usage

Client-certificate behavior is controlled entirely by `SSL_CLIENT_CERT`:

- **A `pkcs11:` URI** (an RFC 7512 subset) selects a PKCS#11 identity:

  ```console
  $ export SSL_CLIENT_CERT='pkcs11:?module-path=/path/to/pkcs11-module.so'
  $ uv pip install --index-url https://my-mtls-index.example.com/simple/ some-package
  ```

  The path attributes `token`, `serial`, `id` (percent-encoded `CKA_ID`), and
  `object` (certificate label) narrow the match when tokens hold more than
  one identity, for example `pkcs11:id=%01` or `pkcs11:token=MyToken`;
  `type=cert` is accepted, other attributes are rejected. The `module-path`
  query attribute must be an absolute path, and the named module is native
  code loaded into the process — only point it at a module you trust.
  Without a `module-path` query attribute, the p11-kit proxy
  (`p11-kit-proxy.so`; `p11-kit-proxy.dylib` on macOS) is loaded, picking up
  any module registered with
  [p11-kit](https://p11-glue.github.io/p11-glue/p11-kit.html). Exactly one
  identity must match, otherwise uv reports an error naming the candidates.

- **A file path** is a PEM client certificate and key, exactly as in
  upstream uv.

- **Unset** means no client certificate — stock uv behavior. PKCS#11 is
  never activated implicitly.

Notes:

- No PIN is presented and no login is performed: the certificate/key pair
  must be visible in a public session (providers unlocked out of band work
  as-is; PIN-protected devices requiring login will not).
  `pin-value`/`pin-source` URI attributes are rejected.
- RSA only (PKCS#1 v1.5 and PSS with SHA-256/384/512); the identity applies
  only to verified HTTPS connections, never to hosts marked
  `--allow-insecure-host`.
- Hash-and-sign mechanisms (`CKM_SHA*_RSA_PKCS`, `CKM_SHA*_RSA_PKCS_PSS`)
  are preferred; providers that only expose the raw `CKM_RSA_PKCS` and
  `CKM_RSA_PKCS_PSS` mechanisms work too, with the handshake transcript
  hashed by uv and the token padding and signing the digest. A provider
  without `CKM_RSA_PKCS_PSS` can only authenticate over TLS 1.2, since
  TLS 1.3 requires RSA-PSS for client certificates. `CKM_RSA_X_509` is not
  used.
- A key's `CKA_ALLOWED_MECHANISMS`, where the provider reports it, narrows
  the mechanisms offered for that key, so a key restricted to part of the
  token's mechanism list advertises only the schemes it can actually
  produce.
- Only the leaf certificate is sent; intermediates must be known to the
  server.
- A certificate is only considered when it can sign as a TLS client: its
  `keyUsage`, if present, must include `digitalSignature` (a
  `keyEncipherment`-only RSA key-exchange certificate is passed over), and
  its `extendedKeyUsage`, if present, must include `clientAuth`. Selecting
  such a certificate explicitly reports the reason; `uv-pkcs11-inspect`
  lists it next to the certificate.

Known limitations (kept simple on purpose; both surface as TLS handshake
failures rather than discovery-time errors):

- Pairing trusts the provider's `CKA_ID` convention: the certificate's
  public key is not compared against the private key, so a stale or
  mispaired certificate sharing the key's `CKA_ID` is selected and fails
  when the server verifies the handshake signature. Re-provision the token
  so certificate and key match.
- Signature schemes are offered based on `C_GetMechanismList` and the key's
  `CKA_ALLOWED_MECHANISMS`: per-mechanism `CKF_SIGN` flags and key-size
  ranges from `C_GetMechanismInfo` are not checked, so a token that lists an
  RSA
  mechanism it cannot use with the selected key (for example a key outside
  the mechanism's supported size range) fails in `C_Sign` during the
  handshake.

### Diagnosing a token

The wheel installs `uv-pkcs11-inspect` next to `uv` and `uvx`. It lists the
tokens, certificates, and private keys a module exposes without login and
applies the same selection rules as uv, so its verdict is what uv will do
with the same URI. Pass the `pkcs11:` URI you intend to put in
`SSL_CLIENT_CERT`, or just a module path; with no argument, the p11-kit proxy
is inspected. The exit status is 0 when exactly one identity would be
selected.

```console
$ uv-pkcs11-inspect 'pkcs11:?module-path=/path/to/pkcs11-module.so'
Module: /path/to/pkcs11-module.so

Token `MyToken` (slot 1)
  Signing: RSA_PSS_SHA512, RSA_PSS_SHA384, RSA_PSS_SHA256, RSA_PKCS1_SHA512, ...
  Certificates: 1
    CKA_ID 3f9a…         label `My Certificate`
  Private keys: 1
    CKA_ID 3f9a…         label `My Key`  RSA_PSS_SHA512, RSA_PSS_SHA384, ...

OK: exactly one usable identity across all tokens: token `MyToken`, CKA_ID 3f9a….
```

With several identities it prints `AMBIGUOUS` and their `CKA_ID`s, so you
can add an `id=`, `token=`, or `object=` attribute to the URI; with none it
prints `NOT USABLE` and a hint (for example that nothing is visible without
login). `uv-pkcs11-inspect --version` reports the fork build it came from.

## Installation

```console
$ pip install uv-pkcs11
```

Releases are published to PyPI at
[pypi.org/project/uv-pkcs11](https://pypi.org/project/uv-pkcs11/). Wheels are built for Linux (x86_64 and aarch64) and macOS (Apple Silicon);
other platforms build from the sdist. The distribution installs the `uv` and
`uvx` commands and therefore **must not be installed alongside the official
`uv` distribution** in the same environment — install one or the other.

## Versioning

Wheel versions mirror the upstream uv release the fork is built from (e.g.
`0.12.9`); a fourth component (`0.12.9.1`, `0.12.9.2`, ...) marks a fork-side
re-release on the same upstream base. Note that PEP 440 treats `0.12.9.0` as
equal to `0.12.9`, so re-releases start at `.1`.
`uv self update` support is intentionally not built in — it would replace
this fork with official uv binaries.

The binary identifies itself as the fork: `uv --version` and `uv self version`
end with a `[uv-pkcs11 <fork version>]` marker naming the exact fork release (the
version itself stays the upstream version so `required-version` checks keep
working), and `uv self version --output-format json`
reports `"package_name": "uv-pkcs11"` and `"fork_version"`, so you can confirm which build is
installed.

```console
$ uv --version
uv 0.12.9 (1a2b3c4d5 2026-09-01 aarch64-apple-darwin) [uv-pkcs11 0.12.9.1]
```

