mirror of
https://github.com/aljazceru/pubky-core.git
synced 2026-01-21 23:14:19 +01:00
Merge branch 'main' into feat/feed
This commit is contained in:
82
Cargo.lock
generated
82
Cargo.lock
generated
@@ -659,6 +659,12 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5"
|
||||
|
||||
[[package]]
|
||||
name = "fiat-crypto"
|
||||
version = "0.2.9"
|
||||
@@ -820,6 +826,12 @@ dependencies = [
|
||||
"byteorder",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
||||
|
||||
[[package]]
|
||||
name = "headers"
|
||||
version = "0.4.0"
|
||||
@@ -914,6 +926,12 @@ version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
||||
|
||||
[[package]]
|
||||
name = "hex"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
|
||||
|
||||
[[package]]
|
||||
name = "http"
|
||||
version = "1.1.0"
|
||||
@@ -1020,6 +1038,16 @@ dependencies = [
|
||||
"unicode-normalization",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indexmap"
|
||||
version = "2.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93ead53efc7ea8ed3cfb0c79fc8023fbb782a5432b52830b6518941cebe6505c"
|
||||
dependencies = [
|
||||
"equivalent",
|
||||
"hashbrown",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inout"
|
||||
version = "0.1.3"
|
||||
@@ -1505,11 +1533,13 @@ dependencies = [
|
||||
"flume",
|
||||
"futures-util",
|
||||
"heed",
|
||||
"hex",
|
||||
"pkarr",
|
||||
"postcard",
|
||||
"pubky-common",
|
||||
"serde",
|
||||
"tokio",
|
||||
"toml",
|
||||
"tower-cookies",
|
||||
"tower-http",
|
||||
"tracing",
|
||||
@@ -1780,6 +1810,15 @@ dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_spanned"
|
||||
version = "0.6.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb5b1b31579f3811bf615c144393417496f152e12ac8b7663bf664f4a815306d"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_urlencoded"
|
||||
version = "0.7.1"
|
||||
@@ -2075,6 +2114,40 @@ dependencies = [
|
||||
"tokio",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"toml_edit",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_datetime"
|
||||
version = "0.6.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||
dependencies = [
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "583c44c02ad26b0c3f3066fe629275e50627026c51ac2e595cca4c230ce1ce1d"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
"toml_datetime",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tower"
|
||||
version = "0.4.13"
|
||||
@@ -2523,6 +2596,15 @@ version = "0.52.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68a9bda4691f099d435ad181000724da8e5899daa10713c2d432552b9ccd3a6f"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winreg"
|
||||
version = "0.52.0"
|
||||
|
||||
@@ -14,11 +14,13 @@ dirs-next = "2.0.0"
|
||||
flume = "0.11.0"
|
||||
futures-util = "0.3.30"
|
||||
heed = "0.20.3"
|
||||
hex = "0.4.3"
|
||||
pkarr = { version = "2.1.0", features = ["async"] }
|
||||
postcard = { version = "1.0.8", features = ["alloc"] }
|
||||
pubky-common = { version = "0.1.0", path = "../pubky-common" }
|
||||
serde = { version = "1.0.204", features = ["derive"] }
|
||||
tokio = { version = "1.37.0", features = ["full"] }
|
||||
toml = "0.8.19"
|
||||
tower-cookies = "0.10.0"
|
||||
tower-http = { version = "0.5.2", features = ["cors", "trace"] }
|
||||
tracing = "0.1.40"
|
||||
|
||||
23
pubky-homeserver/README.md
Normal file
23
pubky-homeserver/README.md
Normal file
@@ -0,0 +1,23 @@
|
||||
# Pubky Homeserver
|
||||
|
||||
## Usage
|
||||
|
||||
Use `cargo run`
|
||||
|
||||
```bash
|
||||
cargo run -- --config=./src/config.toml
|
||||
```
|
||||
|
||||
Or Build first then run from target.
|
||||
|
||||
Build
|
||||
|
||||
```bash
|
||||
cargo build --release
|
||||
```
|
||||
|
||||
Run with an optional config file
|
||||
|
||||
```bash
|
||||
../target/release/pubky-homeserver --config=./src/config.toml
|
||||
```
|
||||
@@ -1,10 +1,14 @@
|
||||
//! Configuration for the server
|
||||
|
||||
use anyhow::{anyhow, Result};
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use pkarr::Keypair;
|
||||
use serde::{Deserialize, Deserializer, Serialize};
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
path::{Path, PathBuf},
|
||||
time::Duration,
|
||||
};
|
||||
use tracing::info;
|
||||
// use serde::{Deserialize, Serialize};
|
||||
use std::{fmt::Debug, path::PathBuf, time::Duration};
|
||||
|
||||
use pubky_common::timestamp::Timestamp;
|
||||
|
||||
@@ -12,10 +16,7 @@ const DEFAULT_HOMESERVER_PORT: u16 = 6287;
|
||||
const DEFAULT_STORAGE_DIR: &str = "pubky";
|
||||
|
||||
/// Server configuration
|
||||
#[derive(
|
||||
// Serialize, Deserialize,
|
||||
Clone,
|
||||
)]
|
||||
#[derive(Serialize, Deserialize, Clone)]
|
||||
pub struct Config {
|
||||
port: Option<u16>,
|
||||
bootstrap: Option<Vec<String>>,
|
||||
@@ -24,20 +25,22 @@ pub struct Config {
|
||||
///
|
||||
/// Defaults to a directory in the OS data directory
|
||||
storage: Option<PathBuf>,
|
||||
keypair: Keypair,
|
||||
#[serde(deserialize_with = "secret_key_deserialize")]
|
||||
secret_key: Option<[u8; 32]>,
|
||||
|
||||
dht_request_timeout: Option<Duration>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
// /// Load the config from a file.
|
||||
// pub async fn load(path: impl AsRef<Path>) -> Result<Config> {
|
||||
// let s = tokio::fs::read_to_string(path.as_ref())
|
||||
// .await
|
||||
// .with_context(|| format!("failed to read {}", path.as_ref().to_string_lossy()))?;
|
||||
// let config: Config = toml::from_str(&s)?;
|
||||
// Ok(config)
|
||||
// }
|
||||
/// Load the config from a file.
|
||||
pub async fn load(path: impl AsRef<Path>) -> Result<Config> {
|
||||
let s = tokio::fs::read_to_string(path.as_ref())
|
||||
.await
|
||||
.with_context(|| format!("failed to read {}", path.as_ref().to_string_lossy()))?;
|
||||
|
||||
let config: Config = toml::from_str(&s)?;
|
||||
Ok(config)
|
||||
}
|
||||
|
||||
/// Testnet configurations
|
||||
pub fn testnet() -> Self {
|
||||
@@ -55,7 +58,6 @@ impl Config {
|
||||
bootstrap,
|
||||
storage,
|
||||
port: Some(15411),
|
||||
keypair: Keypair::from_secret_key(&[0_u8; 32]),
|
||||
dht_request_timeout: Some(Duration::from_millis(10)),
|
||||
..Default::default()
|
||||
}
|
||||
@@ -103,8 +105,8 @@ impl Config {
|
||||
Ok(dir.join("homeserver"))
|
||||
}
|
||||
|
||||
pub fn keypair(&self) -> &Keypair {
|
||||
&self.keypair
|
||||
pub fn keypair(&self) -> Keypair {
|
||||
Keypair::from_secret_key(&self.secret_key.unwrap_or_default())
|
||||
}
|
||||
|
||||
pub(crate) fn dht_request_timeout(&self) -> Option<Duration> {
|
||||
@@ -119,12 +121,34 @@ impl Default for Config {
|
||||
bootstrap: None,
|
||||
domain: "localhost".to_string(),
|
||||
storage: None,
|
||||
keypair: Keypair::random(),
|
||||
secret_key: None,
|
||||
dht_request_timeout: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn secret_key_deserialize<'de, D>(deserializer: D) -> Result<Option<[u8; 32]>, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let opt: Option<String> = Option::deserialize(deserializer)?;
|
||||
|
||||
match opt {
|
||||
Some(s) => {
|
||||
let bytes = hex::decode(s).map_err(serde::de::Error::custom)?;
|
||||
|
||||
if bytes.len() != 32 {
|
||||
return Err(serde::de::Error::custom("Expected a 32-byte array"));
|
||||
}
|
||||
|
||||
let mut arr = [0u8; 32];
|
||||
arr.copy_from_slice(&bytes);
|
||||
Ok(Some(arr))
|
||||
}
|
||||
None => Ok(None),
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for Config {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_map()
|
||||
|
||||
8
pubky-homeserver/src/config.toml
Normal file
8
pubky-homeserver/src/config.toml
Normal file
@@ -0,0 +1,8 @@
|
||||
# Secret key (in hex) to generate the Homeserver's Keypair
|
||||
secret_key = "0000000000000000000000000000000000000000000000000000000000000000"
|
||||
# Domain to be published in Pkarr records for this server to be accessible by.
|
||||
domain = "localhost"
|
||||
# Port for the Homeserver to listen on.
|
||||
port = 6287
|
||||
# Storage directory Defaults to <System's Data Directory>
|
||||
# storage = ""
|
||||
@@ -1,7 +1,7 @@
|
||||
use pkarr::PublicKey;
|
||||
use postcard::{from_bytes, to_allocvec};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tracing::{debug, instrument};
|
||||
use tracing::instrument;
|
||||
|
||||
use heed::{
|
||||
types::{Bytes, Str},
|
||||
@@ -197,8 +197,6 @@ fn next_threshold(
|
||||
reverse: bool,
|
||||
shallow: bool,
|
||||
) -> String {
|
||||
debug!("Fuck me!");
|
||||
|
||||
format!(
|
||||
"{path}{file_or_directory}{}",
|
||||
if file_or_directory.is_empty() {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Result;
|
||||
use pubky_homeserver::{config::Config, Homeserver};
|
||||
|
||||
@@ -8,8 +10,14 @@ struct Cli {
|
||||
/// [tracing_subscriber::EnvFilter]
|
||||
#[clap(short, long)]
|
||||
tracing_env_filter: Option<String>,
|
||||
|
||||
/// Run Homeserver in a local testnet
|
||||
#[clap(long)]
|
||||
testnet: bool,
|
||||
|
||||
/// Optional Path to config file.
|
||||
#[clap(short, long)]
|
||||
config: Option<PathBuf>,
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
@@ -25,8 +33,10 @@ async fn main() -> Result<()> {
|
||||
|
||||
let server = Homeserver::start(if args.testnet {
|
||||
Config::testnet()
|
||||
} else if let Some(config_path) = args.config {
|
||||
Config::load(config_path).await?
|
||||
} else {
|
||||
Default::default()
|
||||
Config::default()
|
||||
})
|
||||
.await?;
|
||||
|
||||
|
||||
@@ -11,6 +11,8 @@ pub async fn publish_server_packet(
|
||||
domain: &str,
|
||||
port: u16,
|
||||
) -> anyhow::Result<()> {
|
||||
// TODO: Try to resolve first before publishing.
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
let mut svcb = SVCB::new(0, domain.try_into()?);
|
||||
|
||||
@@ -30,7 +30,8 @@ impl Homeserver {
|
||||
pub async fn start(config: Config) -> Result<Self> {
|
||||
debug!(?config);
|
||||
|
||||
let public_key = config.keypair().public_key();
|
||||
let keypair = config.keypair();
|
||||
let public_key = keypair.public_key();
|
||||
|
||||
let db = DB::open(&config.storage()?)?;
|
||||
|
||||
@@ -72,7 +73,7 @@ impl Homeserver {
|
||||
|
||||
info!("Homeserver listening on http://localhost:{port}");
|
||||
|
||||
publish_server_packet(pkarr_client, config.keypair(), config.domain(), port).await?;
|
||||
publish_server_packet(pkarr_client, &keypair, config.domain(), port).await?;
|
||||
|
||||
info!("Homeserver listening on pubky://{public_key}");
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
PubkyClient,
|
||||
};
|
||||
|
||||
const MAX_RECURSIVE_PUBKY_HOMESERVER_RESOLUTION: u8 = 3;
|
||||
const MAX_ENDPOINT_RESOLUTION_RECURSION: u8 = 3;
|
||||
|
||||
impl PubkyClient {
|
||||
/// Publish the SVCB record for `_pubky.<public_key>`.
|
||||
@@ -56,24 +56,28 @@ impl PubkyClient {
|
||||
.map_err(|_| Error::Generic("Could not resolve homeserver".to_string()))
|
||||
}
|
||||
|
||||
/// Resolve a service's public_key and clearnet url from a Pubky domain
|
||||
/// Resolve a service's public_key and "non-pkarr url" from a Pubky domain
|
||||
///
|
||||
/// "non-pkarr" url is any URL where the hostname isn't a 52 z-base32 character,
|
||||
/// usually an IPv4, IPv6 or ICANN domain, but could also be any other unknown hostname.
|
||||
///
|
||||
/// Recursively resolve SVCB and HTTPS endpoints, with [MAX_ENDPOINT_RESOLUTION_RECURSION] limit.
|
||||
pub(crate) async fn resolve_endpoint(&self, target: &str) -> Result<Endpoint> {
|
||||
let original_target = target;
|
||||
// TODO: cache the result of this function?
|
||||
|
||||
let mut target = target.to_string();
|
||||
|
||||
let mut homeserver_public_key = None;
|
||||
let mut endpoint_public_key = None;
|
||||
let mut origin = target.clone();
|
||||
|
||||
let mut step = 0;
|
||||
|
||||
// PublicKey is very good at extracting the Pkarr TLD from a string.
|
||||
while let Ok(public_key) = PublicKey::try_from(target.clone()) {
|
||||
if step >= MAX_RECURSIVE_PUBKY_HOMESERVER_RESOLUTION {
|
||||
if step >= MAX_ENDPOINT_RESOLUTION_RECURSION {
|
||||
break;
|
||||
};
|
||||
|
||||
step += 1;
|
||||
|
||||
if let Some(signed_packet) = self
|
||||
@@ -85,8 +89,12 @@ impl PubkyClient {
|
||||
let svcb = signed_packet.resource_records(&target).fold(
|
||||
None,
|
||||
|prev: Option<SVCB>, answer| {
|
||||
if let pkarr::dns::rdata::RData::SVCB(curr) = &answer.rdata {
|
||||
let curr = curr.clone();
|
||||
if let Some(svcb) = match &answer.rdata {
|
||||
pkarr::dns::rdata::RData::SVCB(svcb) => Some(svcb),
|
||||
pkarr::dns::rdata::RData::HTTPS(curr) => Some(&curr.0),
|
||||
_ => None,
|
||||
} {
|
||||
let curr = svcb.clone();
|
||||
|
||||
if curr.priority == 0 {
|
||||
return Some(curr);
|
||||
@@ -106,7 +114,7 @@ impl PubkyClient {
|
||||
);
|
||||
|
||||
if let Some(svcb) = svcb {
|
||||
homeserver_public_key = Some(public_key.clone());
|
||||
endpoint_public_key = Some(public_key.clone());
|
||||
target = svcb.target.to_string();
|
||||
|
||||
if let Some(port) = svcb.get_param(pkarr::dns::rdata::SVCB::PORT) {
|
||||
@@ -120,14 +128,14 @@ impl PubkyClient {
|
||||
origin.clone_from(&target);
|
||||
};
|
||||
|
||||
if step >= MAX_RECURSIVE_PUBKY_HOMESERVER_RESOLUTION {
|
||||
if step >= MAX_ENDPOINT_RESOLUTION_RECURSION {
|
||||
continue;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(public_key) = homeserver_public_key {
|
||||
if let Some(public_key) = endpoint_public_key {
|
||||
let url = Url::parse(&format!(
|
||||
"{}://{}",
|
||||
if origin.starts_with("localhost") {
|
||||
@@ -145,6 +153,7 @@ impl PubkyClient {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Endpoint {
|
||||
pub public_key: PublicKey,
|
||||
pub url: Url,
|
||||
@@ -155,12 +164,104 @@ mod tests {
|
||||
use super::*;
|
||||
|
||||
use pkarr::{
|
||||
dns::{rdata::SVCB, Packet},
|
||||
dns::{
|
||||
rdata::{HTTPS, SVCB},
|
||||
Packet,
|
||||
},
|
||||
mainline::{dht::DhtSettings, Testnet},
|
||||
Keypair, PkarrClient, Settings, SignedPacket,
|
||||
};
|
||||
use pubky_homeserver::Homeserver;
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_endpoint_https() {
|
||||
let testnet = Testnet::new(10);
|
||||
|
||||
let pkarr_client = PkarrClient::new(Settings {
|
||||
dht: DhtSettings {
|
||||
bootstrap: Some(testnet.bootstrap.clone()),
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
})
|
||||
.unwrap()
|
||||
.as_async();
|
||||
|
||||
let domain = "example.com";
|
||||
let mut target;
|
||||
|
||||
// Server
|
||||
{
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let https = HTTPS(SVCB::new(0, domain.try_into().unwrap()));
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
packet.answers.push(pkarr::dns::ResourceRecord::new(
|
||||
"foo".try_into().unwrap(),
|
||||
pkarr::dns::CLASS::IN,
|
||||
60 * 60,
|
||||
pkarr::dns::rdata::RData::HTTPS(https),
|
||||
));
|
||||
|
||||
let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap();
|
||||
|
||||
pkarr_client.publish(&signed_packet).await.unwrap();
|
||||
|
||||
target = format!("foo.{}", keypair.public_key());
|
||||
}
|
||||
|
||||
// intermediate
|
||||
{
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let svcb = SVCB::new(0, target.as_str().try_into().unwrap());
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
packet.answers.push(pkarr::dns::ResourceRecord::new(
|
||||
"bar".try_into().unwrap(),
|
||||
pkarr::dns::CLASS::IN,
|
||||
60 * 60,
|
||||
pkarr::dns::rdata::RData::SVCB(svcb),
|
||||
));
|
||||
|
||||
let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap();
|
||||
|
||||
pkarr_client.publish(&signed_packet).await.unwrap();
|
||||
|
||||
target = format!("bar.{}", keypair.public_key())
|
||||
}
|
||||
|
||||
{
|
||||
let keypair = Keypair::random();
|
||||
|
||||
let svcb = SVCB::new(0, target.as_str().try_into().unwrap());
|
||||
|
||||
let mut packet = Packet::new_reply(0);
|
||||
|
||||
packet.answers.push(pkarr::dns::ResourceRecord::new(
|
||||
"pubky".try_into().unwrap(),
|
||||
pkarr::dns::CLASS::IN,
|
||||
60 * 60,
|
||||
pkarr::dns::rdata::RData::SVCB(svcb),
|
||||
));
|
||||
|
||||
let signed_packet = SignedPacket::from_packet(&keypair, &packet).unwrap();
|
||||
|
||||
pkarr_client.publish(&signed_packet).await.unwrap();
|
||||
|
||||
target = format!("pubky.{}", keypair.public_key())
|
||||
}
|
||||
|
||||
let client = PubkyClient::test(&testnet);
|
||||
|
||||
let endpoint = client.resolve_endpoint(&target).await.unwrap();
|
||||
|
||||
assert_eq!(endpoint.url.host_str().unwrap(), domain);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn resolve_homeserver() {
|
||||
let testnet = Testnet::new(10);
|
||||
|
||||
@@ -63,32 +63,31 @@ impl PubkyClient {
|
||||
pub(crate) async fn pubky_to_http<T: TryInto<Url>>(&self, url: T) -> Result<Url> {
|
||||
let original_url: Url = url.try_into().map_err(|_| Error::InvalidUrl)?;
|
||||
|
||||
if original_url.scheme() != "pubky" {
|
||||
return Ok(original_url);
|
||||
}
|
||||
|
||||
let pubky = original_url
|
||||
.host_str()
|
||||
.ok_or(Error::Generic("Missing Pubky Url host".to_string()))?
|
||||
.to_string();
|
||||
.ok_or(Error::Generic("Missing Pubky Url host".to_string()))?;
|
||||
|
||||
let Endpoint { mut url, .. } = self
|
||||
.resolve_pubky_homeserver(&PublicKey::try_from(pubky.clone())?)
|
||||
.await?;
|
||||
if let Ok(public_key) = PublicKey::try_from(pubky) {
|
||||
let Endpoint { mut url, .. } = self.resolve_pubky_homeserver(&public_key).await?;
|
||||
|
||||
let path = original_url.path_segments();
|
||||
// TODO: remove if we move to subdomains instead of paths.
|
||||
if original_url.scheme() == "pubky" {
|
||||
let path = original_url.path_segments();
|
||||
|
||||
// TODO: replace if we move to subdomains instead of paths.
|
||||
let mut split = url.path_segments_mut().unwrap();
|
||||
split.push(&pubky);
|
||||
if let Some(segments) = path {
|
||||
for segment in segments {
|
||||
split.push(segment);
|
||||
let mut split = url.path_segments_mut().unwrap();
|
||||
split.push(pubky);
|
||||
if let Some(segments) = path {
|
||||
for segment in segments {
|
||||
split.push(segment);
|
||||
}
|
||||
}
|
||||
drop(split);
|
||||
}
|
||||
}
|
||||
drop(split);
|
||||
|
||||
Ok(url)
|
||||
return Ok(url);
|
||||
}
|
||||
|
||||
Ok(original_url)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,30 +10,33 @@ static SPEC_LINE: &str = "pubky.org/recovery";
|
||||
pub fn decrypt_recovery_file(recovery_file: &[u8], passphrase: &str) -> Result<Keypair> {
|
||||
let encryption_key = recovery_file_encryption_key_from_passphrase(passphrase)?;
|
||||
|
||||
let mut split = recovery_file.split(|byte| byte == &10);
|
||||
let newline_index = recovery_file
|
||||
.iter()
|
||||
.position(|&r| r == 10)
|
||||
.ok_or(())
|
||||
.map_err(|_| Error::RecoveryFileMissingSpecLine)?;
|
||||
|
||||
match split.next() {
|
||||
Some(bytes) => {
|
||||
if !(bytes.starts_with(SPEC_LINE.as_bytes())
|
||||
|| bytes.starts_with(b"pkarr.org/recovery"))
|
||||
{
|
||||
return Err(Error::RecoveryFileVersionNotSupported);
|
||||
}
|
||||
}
|
||||
None => return Err(Error::RecoveryFileMissingSpecLine),
|
||||
let spec_line = &recovery_file[..newline_index];
|
||||
|
||||
if !(spec_line.starts_with(SPEC_LINE.as_bytes())
|
||||
|| spec_line.starts_with(b"pkarr.org/recovery"))
|
||||
{
|
||||
return Err(Error::RecoveryFileVersionNotSupported);
|
||||
}
|
||||
|
||||
let encrypted = &recovery_file[newline_index + 1..];
|
||||
|
||||
if encrypted.is_empty() {
|
||||
return Err(Error::RecoverFileMissingEncryptedSecretKey);
|
||||
};
|
||||
|
||||
if let Some(encrypted) = split.next() {
|
||||
let decrypted = decrypt(encrypted, &encryption_key)?;
|
||||
let length = decrypted.len();
|
||||
let secret_key: [u8; 32] = decrypted
|
||||
.try_into()
|
||||
.map_err(|_| Error::RecoverFileInvalidSecretKeyLength(length))?;
|
||||
let decrypted = decrypt(encrypted, &encryption_key)?;
|
||||
let length = decrypted.len();
|
||||
let secret_key: [u8; 32] = decrypted
|
||||
.try_into()
|
||||
.map_err(|_| Error::RecoverFileInvalidSecretKeyLength(length))?;
|
||||
|
||||
return Ok(Keypair::from_secret_key(&secret_key));
|
||||
};
|
||||
|
||||
Err(Error::RecoverFileMissingEncryptedSecretKey)
|
||||
Ok(Keypair::from_secret_key(&secret_key))
|
||||
}
|
||||
|
||||
pub fn create_recovery_file(keypair: &Keypair, passphrase: &str) -> Result<Vec<u8>> {
|
||||
|
||||
Reference in New Issue
Block a user