initial host information endpoint

This commit is contained in:
Jędrzej Stuczyński
2023-10-02 10:52:16 +01:00
parent b8e27c67d9
commit ca6b09ce8d
11 changed files with 155 additions and 27 deletions
+9 -5
View File
@@ -145,8 +145,12 @@ impl PublicKey {
Self::from_bytes(&bytes)
}
pub fn verify(&self, message: &[u8], signature: &Signature) -> Result<(), SignatureError> {
self.0.verify(message, &signature.0)
pub fn verify<M: AsRef<[u8]>>(
&self,
message: M,
signature: &Signature,
) -> Result<(), SignatureError> {
self.0.verify(message.as_ref(), &signature.0)
}
}
@@ -239,16 +243,16 @@ impl PrivateKey {
Self::from_bytes(&bytes)
}
pub fn sign(&self, message: &[u8]) -> Signature {
pub fn sign<M: AsRef<[u8]>>(&self, message: M) -> Signature {
let expanded_secret_key = ed25519_dalek::ExpandedSecretKey::from(&self.0);
let public_key: PublicKey = self.into();
let sig = expanded_secret_key.sign(message, &public_key.0);
let sig = expanded_secret_key.sign(message.as_ref(), &public_key.0);
Signature(sig)
}
/// Signs text with the provided Ed25519 private key, returning a base58 signature
pub fn sign_text(&self, text: &str) -> String {
let signature_bytes = self.sign(text.as_ref()).to_bytes();
let signature_bytes = self.sign(text).to_bytes();
bs58::encode(signature_bytes).into_string()
}
}
+18 -6
View File
@@ -11,8 +11,14 @@ use nym_sphinx::addressing::clients::Recipient;
use nym_task::TaskClient;
fn load_gateway_details(
config: &Config,
_config: &Config,
) -> Result<http::api::v1::gateway::types::Gateway, GatewayError> {
Ok(http::api::v1::gateway::types::Gateway {})
}
fn load_host_details(
config: &Config,
) -> Result<http::api::v1::node::types::HostInformation, GatewayError> {
let identity_public_key: identity::PublicKey = load_public_key(
&config.storage_paths.keys.public_identity_key_file,
"gateway identity",
@@ -23,9 +29,14 @@ fn load_gateway_details(
"gateway sphinx",
)?;
Ok(http::api::v1::gateway::types::Gateway {
encoded_identity_key: identity_public_key.to_base58_string(),
encoded_sphinx_key: sphinx_public_key.to_base58_string(),
Ok(http::api::v1::node::types::HostInformation {
// TODO: this should be extracted differently, i.e. it's the issue of the public/private address
ip_address: vec![config.gateway.listening_address],
hostname: None,
keys: http::api::v1::node::types::HostKeys {
ed25519: identity_public_key.to_base58_string(),
x25519: sphinx_public_key.to_base58_string(),
},
})
}
@@ -80,8 +91,9 @@ pub(crate) fn start_http_api(
// and makes the code a bit nicer to manage. on top of it, all of it will refactored anyway at some point
// (famous last words, eh? - 22.09.23)
let mut config = nym_node::http::Config::new(bin_info_owned!())
.with_gateway(load_gateway_details(gateway_config)?);
let mut config =
nym_node::http::Config::new(bin_info_owned!(), load_host_details(gateway_config)?)
.with_gateway(load_gateway_details(gateway_config)?);
if let Some(nr_config) = network_requester_config {
config = config
+1
View File
@@ -38,4 +38,5 @@ utoipa-swagger-ui = { workspace = true, features = ["axum"] }
nym-bin-common = { path = "../common/bin-common", features = ["openapi"] }
nym-crypto = { path = "../common/crypto", features = ["asymmetric"] }
nym-task = { path = "../common/task" }
+44
View File
@@ -6,7 +6,9 @@ use axum::http::{header, HeaderValue, StatusCode};
use axum::response::{IntoResponse, Response};
use axum::{Json, Router};
use bytes::{BufMut, BytesMut};
use nym_crypto::asymmetric::identity;
use serde::{Deserialize, Serialize};
use std::ops::Deref;
use utoipa::{IntoParams, ToSchema};
pub mod v1;
@@ -32,6 +34,48 @@ pub(super) fn routes(config: Config) -> Router<AppState> {
// data: T,
// }
#[derive(Debug, Clone, ToSchema)]
pub struct SignedResponse<T> {
pub response: T,
pub signature: String,
}
impl<T> SignedResponse<T> {
pub fn new(response: T, key: &identity::PrivateKey) -> serde_json::Result<Self>
where
T: Serialize,
{
let plaintext = serde_json::to_string(&response)?;
let signature = key.sign(plaintext).to_base58_string();
Ok(SignedResponse {
response,
signature,
})
}
pub fn verify(&self, key: &identity::PublicKey) -> bool
where
T: Serialize,
{
let Ok(plaintext) = serde_json::to_string(&self.response) else {
return false;
};
let Ok(signature) = identity::Signature::from_base58_string(&self.signature) else {
return false;
};
key.verify(plaintext, &signature).is_ok()
}
}
impl<T> Deref for SignedResponse<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.response
}
}
#[derive(Debug, Clone, ToSchema)]
pub enum FormattedResponse<T> {
Json(Json<T>),
@@ -6,10 +6,10 @@ use utoipa::ToSchema;
#[derive(Serialize, Debug, Clone, ToSchema)]
pub struct Gateway {
/// Base58 encoded ed25519 EdDSA public key of the gateway used for deriving shared keys with clients
/// and for signing any messages
pub encoded_identity_key: String,
/// Base58-encoded x25519 public key used for sphinx key derivation.
pub encoded_sphinx_key: String,
// /// Base58 encoded ed25519 EdDSA public key of the gateway used for deriving shared keys with clients
// /// and for signing any messages
// pub encoded_identity_key: String,
//
// /// Base58-encoded x25519 public key used for sphinx key derivation.
// pub encoded_sphinx_key: String,
}
@@ -6,9 +6,9 @@ use utoipa::ToSchema;
#[derive(Serialize, Debug, Clone, ToSchema)]
pub struct Mixnode {
/// Base58 encoded ed25519 EdDSA public key of the mixnode.
pub encoded_identity_key: String,
/// Base58-encoded x25519 public key used for sphinx key derivation.
pub encoded_sphinx_key: String,
// /// Base58 encoded ed25519 EdDSA public key of the mixnode.
// pub encoded_identity_key: String,
//
// /// Base58-encoded x25519 public key used for sphinx key derivation.
// pub encoded_sphinx_key: String,
}
@@ -1,2 +1,30 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::http::api::v1::node::types::HostInformation;
use crate::http::api::{FormattedResponse, OutputParams};
use axum::extract::Query;
/// Returns host information of this node.
#[utoipa::path(
get,
path = "/host-information",
context_path = "/api/v1",
tag = "Node",
responses(
(status = 200, content(
("application/json" = HostInformation),
("application/yaml" = HostInformation)
))
),
params(OutputParams)
)]
pub(crate) async fn host_information(
host_information: HostInformation,
Query(output): Query<OutputParams>,
) -> HostInformationResponse {
let output = output.output.unwrap_or_default();
output.to_response(host_information)
}
pub type HostInformationResponse = FormattedResponse<HostInformation>;
+11 -2
View File
@@ -2,8 +2,9 @@
// SPDX-License-Identifier: Apache-2.0
use crate::http::api::v1::node::build_information::build_information;
use crate::http::api::v1::node::host_information::host_information;
use crate::http::api::v1::node::roles::roles;
use crate::http::api::v1::node::types::NodeRoles;
use crate::http::api::v1::node::types::{HostInformation, NodeRoles};
use crate::http::state::AppState;
use axum::routing::get;
use axum::Router;
@@ -17,12 +18,13 @@ pub mod types;
pub(crate) mod routes {
pub(crate) const ROLES: &str = "/roles";
pub(crate) const BUILD_INFO: &str = "/build-information";
// pub(crate) const HOST_INFO: &str = "/host-information";
pub(crate) const HOST_INFO: &str = "/host-information";
}
#[derive(Debug, Clone)]
pub struct Config {
pub build_information: BinaryBuildInformationOwned,
pub host_information: HostInformation,
pub roles: NodeRoles,
}
@@ -42,4 +44,11 @@ pub(super) fn routes(config: Config) -> Router<AppState> {
move |query| roles(node_roles, query)
}),
)
.route(
routes::HOST_INFO,
get({
let host_info = config.host_information;
move |query| host_information(host_info, query)
}),
)
}
@@ -3,6 +3,7 @@
pub(crate) use nym_bin_common::build_information::BinaryBuildInformationOwned;
use serde::Serialize;
use std::net::IpAddr;
use utoipa::ToSchema;
#[derive(Clone, Default, Debug, Copy, ToSchema, Serialize)]
@@ -11,3 +12,25 @@ pub struct NodeRoles {
pub gateway_enabled: bool,
pub network_requester_enabled: bool,
}
#[derive(Clone, Default, Debug, ToSchema, Serialize)]
pub struct HostInformation {
/// Ip address(es) of this host, such as `1.1.1.1`.
pub ip_address: Vec<IpAddr>,
/// Optional hostname of this node, for example `nymtech.net`.
pub hostname: Option<String>,
/// Public keys associated with this node.
pub keys: HostKeys,
}
#[derive(Clone, Default, Debug, ToSchema, Serialize)]
pub struct HostKeys {
/// Base58-encoded ed25519 public key of this node. Currently it corresponds to either mixnode's or gateway's identity.
pub ed25519: String,
/// Base58-encoded x25519 public key of this node used for sphinx/outfox packet creation.
/// Currently it corresponds to either mixnode's or gateway's key.
pub x25519: String,
}
+3 -1
View File
@@ -12,6 +12,7 @@ use utoipa_swagger_ui::SwaggerUi;
info(title = "NymNode API"),
paths(
api::v1::node::build_information::build_information,
api::v1::node::host_information::host_information,
api::v1::node::roles::roles,
api::v1::gateway::root::root_gateway,
api::v1::mixnode::root::root_mixnode,
@@ -21,13 +22,14 @@ use utoipa_swagger_ui::SwaggerUi;
api::Output,
api::OutputParams,
api::v1::node::types::BinaryBuildInformationOwned,
api::v1::node::types::HostInformation,
api::v1::node::types::HostKeys,
api::v1::node::types::NodeRoles,
api::v1::gateway::types::Gateway,
api::v1::mixnode::types::Mixnode,
api::v1::network_requester::types::NetworkRequester,
))
)]
pub(crate) struct ApiDoc;
pub(crate) fn route() -> Router<AppState> {
+7 -2
View File
@@ -5,6 +5,7 @@ use crate::error::NymNodeError;
use crate::http::api::v1::gateway::types::Gateway;
use crate::http::api::v1::mixnode::types::Mixnode;
use crate::http::api::v1::network_requester::types::NetworkRequester;
use crate::http::api::v1::node::types::HostInformation;
use crate::http::middleware::logging;
use crate::http::state::AppState;
use crate::http::NymNodeHTTPServer;
@@ -31,14 +32,18 @@ pub struct Config {
}
impl Config {
pub fn new(binary_info: BinaryBuildInformationOwned) -> Self {
pub fn new(
build_information: BinaryBuildInformationOwned,
host_information: HostInformation,
) -> Self {
Config {
landing: Default::default(),
policy: Default::default(),
api: api::Config {
v1_config: api::v1::Config {
node: api::v1::node::Config {
build_information: binary_info,
build_information,
host_information,
roles: Default::default(),
},
gateway: Default::default(),