Skip to main content

agentplane/testkit/
stub_signer.rs

1//! A signer that is not cryptography.
2//!
3//! Exists so the store conformance battery can check that a backend **keeps**
4//! the signature it was given. That is a persistence question, not a
5//! cryptographic one, and pulling a real signature scheme into the battery would
6//! make every backend author install a crypto dependency to prove their `INSERT`
7//! has the right number of columns.
8//!
9//! Deliberately trivial and deliberately named. Nothing here is unforgeable —
10//! the "signature" is the hash with a fixed byte prepended — and a deployment
11//! that reached for this instead of a real [`Signer`](crate::core::Signer) would
12//! have records that look attested and prove nothing. It lives in `testkit`,
13//! which is off by default and documented as never belonging in a production
14//! build, for exactly that reason.
15
16use crate::core::{CheckpointSigner, Digest, KeyId, SignError, Signer, Verifier};
17
18/// Produces a deterministic, forgeable "signature".
19#[derive(Debug, Clone)]
20pub struct StubSigner {
21    key_id: KeyId,
22}
23
24impl StubSigner {
25    #[must_use]
26    pub fn new(key_id: impl Into<KeyId>) -> Self {
27        Self {
28            key_id: key_id.into(),
29        }
30    }
31}
32
33impl Default for StubSigner {
34    fn default() -> Self {
35        Self::new("testkit://stub")
36    }
37}
38
39impl Signer for StubSigner {
40    fn key_id(&self) -> KeyId {
41        self.key_id.clone()
42    }
43
44    fn sign(&self, hash: &Digest) -> Vec<u8> {
45        let mut out = Vec::with_capacity(33);
46        out.push(0xAB);
47        out.extend_from_slice(hash.as_bytes());
48        out
49    }
50}
51
52/// Also a checkpoint signer, and equally forgeable.
53///
54/// Signs the message rather than a digest of it, matching what a real witness
55/// key does, so a test reaching for this stub exercises production's shape.
56#[async_trait::async_trait]
57impl CheckpointSigner for StubSigner {
58    fn key_id(&self) -> KeyId {
59        self.key_id.clone()
60    }
61
62    async fn sign(&self, message: &[u8]) -> Result<Vec<u8>, SignError> {
63        let mut out = Vec::with_capacity(33);
64        out.push(0xAB);
65        out.extend_from_slice(Digest::of(message).as_bytes());
66        Ok(out)
67    }
68}
69
70impl Verifier for StubSigner {
71    fn verify(&self, key_id: &str, hash: &Digest, signature: &[u8]) -> bool {
72        key_id == self.key_id && signature == Signer::sign(self, hash)
73    }
74}