Skip to main content

ghostkey_common/
lib.rs

1use freenet_stdlib::prelude::*;
2use serde::{Deserialize, Serialize};
3
4#[cfg(feature = "crypto")]
5use ed25519_dalek::VerifyingKey;
6
7/// Compute fingerprint for a ghostkey verifying key.
8/// First 8 bytes of BLAKE3(verifying_key_bytes), base58-encoded.
9#[cfg(feature = "crypto")]
10pub fn fingerprint(verifying_key: &VerifyingKey) -> String {
11    let hash = blake3::hash(verifying_key.as_bytes());
12    bs58::encode(&hash.as_bytes()[..8]).into_string()
13}
14
15/// Serialize a value to CBOR bytes.
16pub fn to_cbor<T: Serialize>(value: &T) -> Result<Vec<u8>, String> {
17    let mut buf = Vec::new();
18    ciborium::into_writer(value, &mut buf).map_err(|e| format!("CBOR serialize: {e}"))?;
19    Ok(buf)
20}
21
22/// Deserialize a value from CBOR bytes.
23pub fn from_cbor<T: for<'de> Deserialize<'de>>(bytes: &[u8]) -> Result<T, String> {
24    ciborium::from_reader(bytes).map_err(|e| format!("CBOR deserialize: {e}"))
25}
26
27/// Who requested a ghostkey operation. Runtime-attested, can't be spoofed.
28#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
29#[non_exhaustive]
30pub enum SignatureRequestor {
31    /// A web application (UI) backed by this contract.
32    WebApp(ContractInstanceId),
33    /// Another delegate on the same node.
34    Delegate(DelegateKey),
35}
36
37/// What the ghostkey delegate actually signs. The raw payload is never signed
38/// alone -- always wrapped with the attested caller identity.
39#[derive(Serialize, Deserialize, Debug, Clone)]
40pub struct ScopedPayload {
41    pub requestor: SignatureRequestor,
42    pub payload: Vec<u8>,
43}
44
45/// Summary info about a stored ghostkey.
46#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
47pub struct GhostKeyInfo {
48    pub fingerprint: String,
49    pub label: Option<String>,
50    /// The notary certificate info field (encodes donation tier).
51    /// Historically called `delegate_info` — the wire-format key is frozen
52    /// via `#[serde(rename)]` for backward compat with stored state.
53    /// See freenet/web#24.
54    #[serde(rename = "delegate_info")]
55    pub notary_info: String,
56}
57
58/// A ghostkey exported for backup (includes private signing key).
59#[derive(Serialize, Deserialize, Debug, Clone)]
60pub struct ExportedGhostKey {
61    pub fingerprint: String,
62    pub certificate_pem: String,
63    pub signing_key_pem: String,
64    pub label: Option<String>,
65    #[serde(rename = "delegate_info")]
66    pub notary_info: String,
67}
68
69/// Requests from UI or other delegates to the ghostkey delegate.
70#[derive(Serialize, Deserialize, Debug, Clone)]
71#[non_exhaustive]
72pub enum GhostkeyRequest {
73    /// Import a ghostkey from PEM-armored certificate and signing key.
74    /// If master_verifying_key_pem is None, uses the hardcoded Freenet master key.
75    ImportGhostKey {
76        certificate_pem: String,
77        signing_key_pem: String,
78        #[serde(default)]
79        master_verifying_key_pem: Option<String>,
80    },
81    /// List all stored ghostkeys.
82    ListGhostKeys,
83    /// Get details for a specific ghostkey.
84    GetGhostKey { fingerprint: String },
85    /// Get just the public certificate (for sharing with counterparties).
86    GetCertificate { fingerprint: String },
87    /// Delete a stored ghostkey.
88    DeleteGhostKey { fingerprint: String },
89    /// Set a user-friendly label.
90    SetLabel { fingerprint: String, label: String },
91    /// Sign a message with a specific ghostkey. The delegate scopes the
92    /// signature to the requestor.
93    SignMessage {
94        fingerprint: String,
95        message: Vec<u8>,
96    },
97    /// Sign a message with the user's default ghostkey (highest-tier key,
98    /// or user-overridden via SetDefaultKey). Apps should prefer this over
99    /// SignMessage -- it avoids needing to know about specific fingerprints.
100    SignWithDefault { message: Vec<u8> },
101    /// Set which ghostkey is the default for signing.
102    SetDefaultKey { fingerprint: String },
103    /// Get the current default ghostkey fingerprint.
104    GetDefaultKey,
105    /// Verify a signed message produced by this delegate.
106    VerifySignedMessage { signed_message: Vec<u8> },
107    /// Export a ghostkey's certificate and signing key for backup.
108    /// Security-sensitive: returns the private signing key.
109    ExportGhostKey { fingerprint: String },
110    /// Export all ghostkeys for backup.
111    ExportAllGhostKeys,
112    /// Grant an application or delegate permission to use a ghostkey.
113    GrantPermission {
114        fingerprint: String,
115        requestor: SignatureRequestor,
116    },
117    /// Revoke a previously granted permission.
118    RevokePermission {
119        fingerprint: String,
120        requestor: SignatureRequestor,
121    },
122    /// List permissions for a ghostkey.
123    ListPermissions { fingerprint: String },
124    /// Debug: force a permission prompt regardless of existing permissions.
125    TestPermissionPrompt { fingerprint: String },
126}
127
128/// Responses from the ghostkey delegate.
129#[derive(Serialize, Deserialize, Debug, Clone)]
130#[non_exhaustive]
131pub enum GhostkeyResponse {
132    ImportResult {
133        fingerprint: String,
134        #[serde(rename = "delegate_info")]
135        notary_info: String,
136    },
137    GhostKeyList {
138        keys: Vec<GhostKeyInfo>,
139    },
140    GhostKeyDetail {
141        fingerprint: String,
142        certificate_pem: String,
143        label: Option<String>,
144        #[serde(rename = "delegate_info")]
145        notary_info: String,
146    },
147    Certificate {
148        fingerprint: String,
149        certificate_pem: String,
150    },
151    SignResult {
152        /// CBOR-serialized ScopedPayload
153        scoped_payload: Vec<u8>,
154        /// Ed25519 signature over the scoped_payload bytes
155        signature: Vec<u8>,
156        /// The certificate PEM, so the verifier has the full chain
157        certificate_pem: String,
158    },
159    DefaultKeyResult {
160        fingerprint: Option<String>,
161    },
162    DefaultKeySet {
163        fingerprint: String,
164    },
165    VerifyResult {
166        valid: bool,
167        signer_fingerprint: Option<String>,
168        #[serde(rename = "delegate_info")]
169        notary_info: Option<String>,
170        requestor: Option<SignatureRequestor>,
171        message: Option<Vec<u8>>,
172    },
173    Deleted {
174        fingerprint: String,
175    },
176    LabelSet {
177        fingerprint: String,
178        label: String,
179    },
180    PermissionGranted {
181        fingerprint: String,
182        requestor: SignatureRequestor,
183    },
184    PermissionRevoked {
185        fingerprint: String,
186        requestor: SignatureRequestor,
187    },
188    PermissionList {
189        fingerprint: String,
190        requestors: Vec<SignatureRequestor>,
191    },
192    ExportResult {
193        fingerprint: String,
194        certificate_pem: String,
195        signing_key_pem: String,
196        label: Option<String>,
197    },
198    ExportAllResult {
199        keys: Vec<ExportedGhostKey>,
200    },
201    PermissionDenied {
202        fingerprint: String,
203        requestor: SignatureRequestor,
204    },
205    /// The user has no ghostkeys. Apps should direct the user to
206    /// freenet.org/ghostkey to purchase one.
207    NoIdentityAvailable,
208    /// The requested ghostkey fingerprint was not found.
209    KeyNotFound {
210        fingerprint: String,
211    },
212    /// Generic error for unexpected failures.
213    Error {
214        message: String,
215    },
216}