Cyphrme / Cyphrme/Cyphr

A principal's genesis key cannot be revoked out of band

Open
#169 0 comments 0 reactions 0 assignees View on GitHub
bug security
Dominant language
Rust
Stars
3
Forks
0
Avg merge
1d 42m
Merged PRs (30d)
11

Description

A principal's genesis key cannot be revoked out of band. `POST /revoke` refuses it, because the presence check the handler performs never sees a genesis key.

This is the key most likely to be on a device a user has just lost, and for any principal whose owner never added a second key it is the only key.

## Reproduction

Create a principal with `cyphr key generate` and `cyphr key add`, push its commit to a server, then send a well-formed self-signed naked revoke for each of its two keys.

For the key introduced by the `key/create` transaction:

```
POST /revoke -> HTTP 200
{"revoked_tmb":"DUSuV3vtbanIMa618ZzRO6aFJRrWMIv0VVpeQX4gWNY","recorded":true}
```

A later login by that key is refused, as intended:

```
POST /auth/login -> HTTP 401 {"error":"signing key was naked-revoked"}
```

For the principal's genesis key — the signer of that same `key/create`, and the signer of the closing `commit/create`:

```
POST /revoke -> HTTP 400
{"error":"revoke names a key this server has not indexed"}
```

Reproduced on two independently created principals. In both, the key the server accepts is the one named by `key/create`'s `id`; the key named by the transaction's `tmb` — the genesis signer — is refused.

## Cause

`rs/cyphr-server/src/revoke.rs:151` gates on `indexer.get_key(&tmb_b64)` returning `Some`. A genesis key never lands in that index: it arrives embedded on a commit rather than as the target of a `key/create`.

## Why it matters

Naked revoke is the mechanism a holder uses to declare a key compromised out of band, and its whole value is that it works when the principal's chain cannot be extended — which is exactly the situation after a device is lost. For principals at the lower assurance levels, and for any principal whose owner never added a second key, the genesis key is the only key, so the mechanism is unavailable precisely where it is most needed.

The error message compounds it: "has not indexed" reads as a problem with the request, when the problem is the key class.

## The fix is a design call

Either the presence check should treat a key reachable from stored commit state as seen, or genesis keys should be indexed on ingest. Both are defensible and they have different consequences for what the index means. What is not defensible is the current state, where the one key class that most needs an emergency kill switch does not have one.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start at rs/cyphr-server/src/revoke.rs:151 and trace indexer.get_key(&tmb_b64), then inspect how genesis keys enter stored commit state and how key/create keys are indexed. Reproduce the two POST /revoke cases described in the issue. Done means the agreed design permits appropriate genesis-key revocation and includes coverage for the genesis and ordinary key paths.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
api, authentication, backend, security
Issue type
Bug
Difficulty
5/5
Estimated time
Over a week
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.