Skip to content

[PQ-3] switchboard/pq_keys.py — keypair generate/load/save + key-id derivation #36

@Pattermesh

Description

@Pattermesh

Part of #33. Built on top of #TBD (PQ-2).

Deliverables

New module switchboard/pq_keys.py:

@dataclass
class PQKeyPair:
    alg: str
    sk: bytes
    pk: bytes
    key_id: str   # sha256(pk)[:16] hex

    @classmethod
    def generate(cls, alg: str = "ml-dsa-65") -> "PQKeyPair": ...
    @classmethod
    def load(cls, path: str, passphrase: bytes | None = None) -> "PQKeyPair": ...
    def save(self, path: str, passphrase: bytes | None = None) -> None: ...
    def sign(self, transcript: bytes) -> bytes: ...

def verify(alg: str, pk: bytes, transcript: bytes, sig: bytes) -> bool: ...

Storage format on disk: PEM-like envelope:

-----BEGIN SWITCHBOARD PQ KEY-----
alg: ml-dsa-65
kdf: scrypt
kdf-salt: <hex>
kdf-n: 32768
kdf-r: 8
kdf-p: 1
cipher: chacha20-poly1305
cipher-nonce: <hex>
cipher-tag: <hex>
<base64 ciphertext of sk>
-----END SWITCHBOARD PQ KEY-----

Public-key file is a separate .pub with the raw pk + alg header.

Tests

  • Generate, save, load round-trip.
  • Wrong passphrase → loud failure, not silent corruption.
  • key_id deterministic and stable across runs.

Done when

  • tests/test_pq_keys.py green.
  • pq_keys.py is import-safe without liboqs (raises only at .generate()/.sign() time).

Depends on: #TBD (PQ-2).

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions