git-ents.gitmain
⌘K
foforge
signing.rs82 lines · 2.9 KB · rusthistorycomment on this file
1//! The signing identity the composition root injects into the lens
2//! (`lens.serve`, `roots.web-agnostic`).
3//!
4//! The lens writes new comments through the same signed mutation path
5//! every other frontend uses (`lens.parity`), so it must be handed an
6//! identity to sign with — but, exactly like `ents-web`, it resolves no
7//! key itself and assumes nothing about which editor (if any) is attached.
8//! [`Signing`] is a plain owned value the root builds once and moves in;
9//! there is no second implementation to abstract over the way `ents-web`'s
10//! hosted/local split needs, because a lens only ever serves the local
11//! root (`lens.serve`), so a concrete carrier is enough and no trait is
12//! introduced.
13
14/// A closure that signs a commit's to-be-signed bytes, producing an armored
15/// SSHSIG PEM block — the injected half of [`Signing`].
16pub type SignFn = Box<dyn Fn(&[u8]) -> String>;
17
18/// An owned signing identity: the commit author signature and a closure
19/// that produces an SSHSIG armored block for a commit's bytes, plus the
20/// public key that identifies the acting member.
21///
22/// Built by the composition root from the user's own key (the same
23/// resolution `git ents comment` and `git ents serve` perform) and moved
24/// into the [`crate::Lens`]; the lens never resolves a key path or reads
25/// `user.signingkey` itself.
26///
27/// # Examples
28///
29/// ```
30/// use ents_lens::Signing;
31///
32/// let signing = Signing::new(
33/// gix::actor::Signature {
34/// name: "jdc".into(),
35/// email: "jdc@ents.test".into(),
36/// time: gix::date::Time { seconds: 0, offset: 0 },
37/// },
38/// Box::new(|_payload| "-----BEGIN SSH SIGNATURE-----\n-----END SSH SIGNATURE-----\n".to_owned()),
39/// "ssh-ed25519 AAAA... jdc".to_owned(),
40/// );
41/// assert_eq!(signing.actor().name, "jdc");
42/// ```
43pub struct Signing {
44 actor: gix::actor::Signature,
45 sign: SignFn,
46 public_openssh: String,
47}
48
49impl Signing {
50 /// Build a signing identity from an already-resolved key: the commit
51 /// `actor` signature, a `sign` closure over the key, and the key's
52 /// `public_openssh` single-line form.
53 #[must_use]
54 pub fn new(actor: gix::actor::Signature, sign: SignFn, public_openssh: String) -> Self {
55 Self {
56 actor,
57 sign,
58 public_openssh,
59 }
60 }
61
62 /// The commit author/committer signature every comment mutation this
63 /// identity signs will carry.
64 #[must_use]
65 pub fn actor(&self) -> gix::actor::Signature {
66 self.actor.clone()
67 }
68
69 /// The public half of this identity's key, in OpenSSH single-line
70 /// form — which enrolled member is acting.
71 #[must_use]
72 pub fn public_openssh(&self) -> &str {
73 &self.public_openssh
74 }
75
76 /// Sign `payload` (a commit's to-be-signed bytes), returning the
77 /// armored SSHSIG PEM block for the commit's `gpgsig` header.
78 #[must_use]
79 pub fn sign(&self, payload: &[u8]) -> String {
80 (self.sign)(payload)
81 }
82}