Skip to main content

iroh_services/
caps.rs

1use anyhow::Result;
2use iroh::{EndpointId, SecretKey};
3use iroh_services_proto::caps::{
4    Caps as ProtoCaps, NetDiagnosticsCap as ProtoNetDiagnosticsCap, RelayCap as ProtoRelayCap,
5};
6use n0_future::time::Duration;
7use rcan::{Expires, Rcan};
8
9use crate::ApiToken;
10
11/// Capabilities accepted by iroh-services.
12///
13/// Construct a capability set with one of the provided methods. The underlying
14/// wire representation is private to this crate.
15#[derive(Debug, Eq, PartialEq, Clone)]
16pub struct Caps(pub(crate) ProtoCaps);
17
18impl Caps {
19    /// Returns the capabilities granted by a shared API secret.
20    pub fn client() -> Self {
21        Self(ProtoCaps::for_shared_secret())
22    }
23
24    /// Returns permission to use an iroh-services relay.
25    pub fn relay_use() -> Self {
26        Self(ProtoCaps::new([ProtoRelayCap::Use]))
27    }
28
29    /// Returns permission to request network diagnostics from an endpoint.
30    pub fn net_diagnostics_get_any() -> Self {
31        Self(ProtoCaps::new([ProtoNetDiagnosticsCap::GetAny]))
32    }
33}
34
35pub(crate) const DEFAULT_CAP_EXPIRY: Duration = Duration::from_hours(24 * 30); // 1 month
36
37/// Create an rcan token for the api access from a PEM-encoded OpenSSH ed25519
38/// private key.
39#[cfg(not(wasm_browser))]
40pub fn create_api_token_from_openssh_pem(
41    pem: &str,
42    local_id: EndpointId,
43    max_age: Duration,
44    capability: Caps,
45) -> Result<ApiToken> {
46    let seed = crate::openssh::parse_ed25519_private_key(pem)?;
47    let issuer = ed25519_dalek::SigningKey::from_bytes(&seed);
48    let audience = local_id.as_verifying_key();
49    let can =
50        Rcan::issuing_builder(&issuer, audience, capability.0).sign(Expires::valid_for(max_age));
51    Ok(ApiToken::new(can))
52}
53
54/// Create an rcan token that grants capabilities to a remote endpoint.
55/// The local endpoint is the issuer (granter), and the remote endpoint is the
56/// audience (grantee).
57pub fn create_grant_token(
58    local_secret: SecretKey,
59    remote_id: EndpointId,
60    max_age: Duration,
61    capability: Caps,
62) -> Result<ApiToken> {
63    let issuer = ed25519_dalek::SigningKey::from_bytes(&local_secret.to_bytes());
64    let audience = remote_id.as_verifying_key();
65    let can =
66        Rcan::issuing_builder(&issuer, audience, capability.0).sign(Expires::valid_for(max_age));
67    Ok(ApiToken::new(can))
68}
69
70/// Create an rcan token for the api access from an iroh secret key
71pub fn create_api_token_from_secret_key(
72    private_key: SecretKey,
73    local_id: EndpointId,
74    max_age: Duration,
75    capability: Caps,
76) -> Result<ApiToken> {
77    let issuer = ed25519_dalek::SigningKey::from_bytes(&private_key.to_bytes());
78    let audience = local_id.as_verifying_key();
79    let can =
80        Rcan::issuing_builder(&issuer, audience, capability.0).sign(Expires::valid_for(max_age));
81    Ok(ApiToken::new(can))
82}