> ## Documentation Index
> Fetch the complete documentation index at: https://docs.parmanasystems.com/llms.txt
> Use this file to discover all available pages before exploring further.

# A key for one algorithm only: the key/algorithm binding guard

> Why a signer configured for one signature algorithm must refuse a key made for another, and the small check in Parmana that makes it refuse.

Say, for example, that a lending startup in Pune decides to sign its agent records with ML-DSA-65, the post-quantum
algorithm, because a regulator asked how long its evidence will stay trustworthy. An engineer changes one setting. The
deploy goes out. Every record now says "signed with ML-DSA-65". The compliance team writes it into a board note: from
this week, the record of a ₹2,00,000 loan disbursement proposed by an agent carries a post-quantum signature.

Nobody noticed that the key directory still held the old Ed25519 key files. The setting changed. The keys did not.

What happens next depends on one small detail of how signing libraries work, and it is the detail this essay is about.

## The library trusts the key, not your setting

In Node's crypto library, the sign and verify functions look at the key they are handed and use whatever algorithm that
key belongs to. Give them an Ed25519 key and you get an Ed25519 signature, whatever your configuration says.

So in the scenario above, nothing fails. The code labelled "ML-DSA-65" calls sign, the library sees an Ed25519 key, and
it quietly produces an Ed25519 signature. The record says one algorithm. The bytes are another. The board note is
wrong, and no error, test or log line says so.

This is the worst kind of security bug: everything works, and the protection you think you bought is not there.

## The idea: the algorithm is chosen once, and the key must agree

The fix is not clever. Whoever chose the algorithm, the operator, in configuration, should win. The key is just
material. If the material does not match the choice, stop.

In other words, a key is for one algorithm only, and a signer configured for an algorithm accepts only keys of that
algorithm. Never "use whatever the key is".

## How Parmana does it

Parmana has two signature providers: Ed25519, the default, and ML-DSA-65 (the code calls it `dilithium3` for
historical reasons, and accepts `ml-dsa-65` as an alias). Both run one check before every sign and every verify:
the key's own type must equal the type the provider expects.

If it does not, the call fails closed with an error that names both: the type it expected and the type it received,
and a sentence saying the configured algorithm does not match the key material. It does not fall back, retry with the
other provider, or sign anyway. The check runs in both directions: an Ed25519 key handed to the ML-DSA-65 provider is
refused, and so is an ML-DSA-65 key handed to the Ed25519 provider.

This is recorded as claim 2.13 in the claims register. It is covered by the signature provider unit tests and by a
runnable tutorial that tries both wrong pairings and then shows a correctly matched key still working. The same check
ships in the separate open-source package, `@parmana/sign`, which raises an error when a key's type does not match the
provider.

There are neighbours to this check that come from the same instinct, refuse rather than guess:

* **Selecting ML-DSA-65 with missing or mismatched key material fails closed.** It does not regenerate a key or
  substitute a different one.
* **Key ids are checked before they become file paths.** Local keys live as files named after their key id. A key id
  must match a short set of safe characters (letters, digits, dot, underscore and hyphen), so an id containing "../"
  cannot point the key reader at some other file on the disk (claim 2.18).
* **An unbuilt key store fails at startup.** The configuration accepts names for Azure Key Vault, Google Cloud KMS and
  HSMs, but none is implemented. Setting one makes the server refuse to start, naming the value. Before this was
  fixed, setting the AWS KMS value quietly gave you key files on disk, with no error at all.
* **The AWS KMS signer accepts only Ed25519 KMS keys.** Any other kind of KMS key is refused.

## What it does not do

* **It checks the type of the key, not whose key it is.** An Ed25519 key passes the Ed25519 provider whether it is your
  current key, a revoked one or a stranger's. Which key is acceptable is a separate question, answered by key ids,
  expiry and revocation, and by the public key a verifier chooses to trust.
* **It does not change algorithms for you.** A verifying process supports one configured primary algorithm at a time.
  Moving a live deployment from Ed25519 to ML-DSA-65 while still verifying old records is not built; it is listed as
  future work.
* **ML-DSA-65 is not running in production.** Every Parmana deployment today, production included, signs with Ed25519.
  The guard matters most at the moment someone switches, which no deployment has done yet.
* **It is not a check on configuration as a whole.** Startup validation catches some wrong values, but a correctly
  typed key with the wrong contents, such as a public key that does not belong to the private key, is caught by other
  checks, not this one.

## The takeaway

A signature label is a promise about the bytes beneath it. If the code can sign with one algorithm while writing down
another, the label is decoration. The guard is a few lines long, and its job is to make that mismatch loud: one
algorithm chosen, one kind of key accepted, and an error that tells you exactly which two disagreed.

**Read more:** [Cryptography](/cryptography/overview),
[Choose a signature provider](/guides/choose-a-signature-provider),
[Choosing signatures](/essays/19-choosing-signatures).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.