tfclient is done! woo!
This commit is contained in:
parent
7513eb8318
commit
fa40dcda5b
|
@ -622,7 +622,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dnapi-rs"
|
name = "dnapi-rs"
|
||||||
version = "0.1.6"
|
version = "0.1.7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"base64 0.21.0",
|
"base64 0.21.0",
|
||||||
"base64-serde",
|
"base64-serde",
|
||||||
|
@ -2803,7 +2803,7 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "trifid-pki"
|
name = "trifid-pki"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"ed25519-dalek",
|
"ed25519-dalek",
|
||||||
"hex",
|
"hex",
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "dnapi-rs"
|
name = "dnapi-rs"
|
||||||
version = "0.1.6"
|
version = "0.1.7"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A rust client for the Defined Networking API"
|
description = "A rust client for the Defined Networking API"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
|
@ -11,6 +11,7 @@ use crate::credentials::{Credentials, ed25519_public_keys_from_pem};
|
||||||
use crate::crypto::{new_keys, nonce};
|
use crate::crypto::{new_keys, nonce};
|
||||||
use crate::message::{CHECK_FOR_UPDATE, CheckForUpdateResponseWrapper, DO_UPDATE, DoUpdateRequest, DoUpdateResponse, ENDPOINT_V1, ENROLL_ENDPOINT, EnrollRequest, EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper};
|
use crate::message::{CHECK_FOR_UPDATE, CheckForUpdateResponseWrapper, DO_UPDATE, DoUpdateRequest, DoUpdateResponse, ENDPOINT_V1, ENROLL_ENDPOINT, EnrollRequest, EnrollResponse, RequestV1, RequestWrapper, SignedResponseWrapper};
|
||||||
use serde::{Serialize, Deserialize};
|
use serde::{Serialize, Deserialize};
|
||||||
|
use base64::Engine;
|
||||||
|
|
||||||
/// A type alias to abstract return types
|
/// A type alias to abstract return types
|
||||||
pub type NebulaConfig = Vec<u8>;
|
pub type NebulaConfig = Vec<u8>;
|
||||||
|
@ -185,12 +186,18 @@ impl Client {
|
||||||
timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(),
|
timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(),
|
||||||
})?;
|
})?;
|
||||||
let encoded_msg_bytes = encoded_msg.into_bytes();
|
let encoded_msg_bytes = encoded_msg.into_bytes();
|
||||||
let signature = ed_privkey.sign(&encoded_msg_bytes).to_vec();
|
let b64_msg = base64::engine::general_purpose::STANDARD.encode(encoded_msg_bytes);
|
||||||
|
let b64_msg_bytes = b64_msg.as_bytes();
|
||||||
|
let signature = ed_privkey.sign(b64_msg_bytes).to_vec();
|
||||||
|
|
||||||
|
ed_privkey.verify(b64_msg_bytes, &Signature::from_slice(&signature)?)?;
|
||||||
|
debug!("signature valid via clientside check");
|
||||||
|
|
||||||
let body = RequestV1 {
|
let body = RequestV1 {
|
||||||
version: 1,
|
version: 1,
|
||||||
host_id: host_id.to_string(),
|
host_id: host_id.to_string(),
|
||||||
counter,
|
counter,
|
||||||
message: encoded_msg_bytes,
|
message: b64_msg,
|
||||||
signature,
|
signature,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
//! Client structs to handle communication with the Defined Networking API. This is the blocking client API - if you want async instead, set no-default-features and enable the async feature instead.
|
//! Client structs to handle communication with the Defined Networking API. This is the blocking client API - if you want async instead, set no-default-features and enable the async feature instead.
|
||||||
|
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
use base64::Engine;
|
||||||
use chrono::Local;
|
use chrono::Local;
|
||||||
use log::{debug, error};
|
use log::{debug, error, trace};
|
||||||
use reqwest::StatusCode;
|
use reqwest::StatusCode;
|
||||||
use url::Url;
|
use url::Url;
|
||||||
use trifid_pki::cert::serialize_ed25519_public;
|
use trifid_pki::cert::serialize_ed25519_public;
|
||||||
|
@ -90,6 +91,9 @@ impl Client {
|
||||||
organization_name: r.organization.name,
|
organization_name: r.organization.name,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
debug!("parsing public keys");
|
||||||
|
|
||||||
|
|
||||||
let trusted_keys = ed25519_public_keys_from_pem(&r.trusted_keys)?;
|
let trusted_keys = ed25519_public_keys_from_pem(&r.trusted_keys)?;
|
||||||
|
|
||||||
let creds = Credentials {
|
let creds = Credentials {
|
||||||
|
@ -190,17 +194,25 @@ impl Client {
|
||||||
timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(),
|
timestamp: Local::now().format("%Y-%m-%dT%H:%M:%S.%f%:z").to_string(),
|
||||||
})?;
|
})?;
|
||||||
let encoded_msg_bytes = encoded_msg.into_bytes();
|
let encoded_msg_bytes = encoded_msg.into_bytes();
|
||||||
let signature = ed_privkey.sign(&encoded_msg_bytes).to_vec();
|
let b64_msg = base64::engine::general_purpose::STANDARD.encode(encoded_msg_bytes);
|
||||||
|
let b64_msg_bytes = b64_msg.as_bytes();
|
||||||
|
let signature = ed_privkey.sign(b64_msg_bytes).to_vec();
|
||||||
|
|
||||||
|
ed_privkey.verify(b64_msg_bytes, &Signature::from_slice(&signature)?)?;
|
||||||
|
debug!("signature valid via clientside check");
|
||||||
|
|
||||||
let body = RequestV1 {
|
let body = RequestV1 {
|
||||||
version: 1,
|
version: 1,
|
||||||
host_id: host_id.to_string(),
|
host_id: host_id.to_string(),
|
||||||
counter,
|
counter,
|
||||||
message: encoded_msg_bytes,
|
message: b64_msg,
|
||||||
signature,
|
signature,
|
||||||
};
|
};
|
||||||
|
|
||||||
let post_body = serde_json::to_string(&body)?;
|
let post_body = serde_json::to_string(&body)?;
|
||||||
|
|
||||||
|
trace!("sending dnclient request {}", post_body);
|
||||||
|
|
||||||
let resp = self.http_client.post(self.server_url.join(ENDPOINT_V1)?).body(post_body).send()?;
|
let resp = self.http_client.post(self.server_url.join(ENDPOINT_V1)?).body(post_body).send()?;
|
||||||
|
|
||||||
match resp.status() {
|
match resp.status() {
|
||||||
|
|
|
@ -23,9 +23,8 @@ pub struct RequestV1 {
|
||||||
pub host_id: String,
|
pub host_id: String,
|
||||||
/// The counter last returned by the server
|
/// The counter last returned by the server
|
||||||
pub counter: u32,
|
pub counter: u32,
|
||||||
#[serde(with = "Base64Standard")]
|
/// A base64-encoded message. This must be previously base64-encoded, as the signature is signed over the base64-encoded data.
|
||||||
/// A base64-encoded message
|
pub message: String,
|
||||||
pub message: Vec<u8>,
|
|
||||||
#[serde(with = "Base64Standard")]
|
#[serde(with = "Base64Standard")]
|
||||||
/// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key
|
/// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key
|
||||||
pub signature: Vec<u8>
|
pub signature: Vec<u8>
|
||||||
|
|
|
@ -24,7 +24,7 @@ base64 = "0.21.0"
|
||||||
chrono = "0.4.24"
|
chrono = "0.4.24"
|
||||||
ipnet = "2.7.1"
|
ipnet = "2.7.1"
|
||||||
base64-serde = "0.7.0"
|
base64-serde = "0.7.0"
|
||||||
dnapi-rs = { version = "0.1.6", path = "../dnapi-rs" }
|
dnapi-rs = { version = "0.1.7", path = "../dnapi-rs" }
|
||||||
serde_yaml = "0.9.19"
|
serde_yaml = "0.9.19"
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
|
|
|
@ -145,7 +145,9 @@ pub struct NebulaConfig {
|
||||||
pub struct NebulaConfigPki {
|
pub struct NebulaConfigPki {
|
||||||
pub ca: String,
|
pub ca: String,
|
||||||
pub cert: String,
|
pub cert: String,
|
||||||
pub key: String,
|
#[serde(default = "none")]
|
||||||
|
#[serde(skip_serializing_if = "is_none")]
|
||||||
|
pub key: Option<String>,
|
||||||
#[serde(default = "empty_vec")]
|
#[serde(default = "empty_vec")]
|
||||||
#[serde(skip_serializing_if = "is_empty_vec")]
|
#[serde(skip_serializing_if = "is_empty_vec")]
|
||||||
pub blocklist: Vec<String>,
|
pub blocklist: Vec<String>,
|
||||||
|
@ -180,7 +182,9 @@ pub struct NebulaConfigLighthouse {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct NebulaConfigLighthouseDns {
|
pub struct NebulaConfigLighthouseDns {
|
||||||
pub host: Ipv4Addr,
|
#[serde(default = "string_empty")]
|
||||||
|
#[serde(skip_serializing_if = "is_string_empty")]
|
||||||
|
pub host: String,
|
||||||
#[serde(default = "u16_53")]
|
#[serde(default = "u16_53")]
|
||||||
#[serde(skip_serializing_if = "is_u16_53")]
|
#[serde(skip_serializing_if = "is_u16_53")]
|
||||||
pub port: u16
|
pub port: u16
|
||||||
|
@ -188,9 +192,9 @@ pub struct NebulaConfigLighthouseDns {
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Clone, Debug)]
|
#[derive(Serialize, Deserialize, Clone, Debug)]
|
||||||
pub struct NebulaConfigListen {
|
pub struct NebulaConfigListen {
|
||||||
#[serde(default = "ipv4_0000")]
|
#[serde(default = "string_empty")]
|
||||||
#[serde(skip_serializing_if = "is_ipv4_0000")]
|
#[serde(skip_serializing_if = "is_string_empty")]
|
||||||
pub host: Ipv4Addr,
|
pub host: String,
|
||||||
#[serde(default = "u16_0")]
|
#[serde(default = "u16_0")]
|
||||||
#[serde(skip_serializing_if = "is_u16_0")]
|
#[serde(skip_serializing_if = "is_u16_0")]
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
@ -520,6 +524,9 @@ fn is_u64_1(u: &u64) -> bool { *u == 1 }
|
||||||
fn string_nebula() -> String { "nebula".to_string() }
|
fn string_nebula() -> String { "nebula".to_string() }
|
||||||
fn is_string_nebula(s: &str) -> bool { s == "nebula" }
|
fn is_string_nebula(s: &str) -> bool { s == "nebula" }
|
||||||
|
|
||||||
|
fn string_empty() -> String { String::new() }
|
||||||
|
fn is_string_empty(s: &str) -> bool { s == "" }
|
||||||
|
|
||||||
fn protocol_tcp() -> NebulaConfigStatsGraphiteProtocol { NebulaConfigStatsGraphiteProtocol::Tcp }
|
fn protocol_tcp() -> NebulaConfigStatsGraphiteProtocol { NebulaConfigStatsGraphiteProtocol::Tcp }
|
||||||
fn is_protocol_tcp(p: &NebulaConfigStatsGraphiteProtocol) -> bool { matches!(p, NebulaConfigStatsGraphiteProtocol::Tcp) }
|
fn is_protocol_tcp(p: &NebulaConfigStatsGraphiteProtocol) -> bool { matches!(p, NebulaConfigStatsGraphiteProtocol::Tcp) }
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::sync::mpsc::{Receiver, TryRecvError};
|
use std::sync::mpsc::{Receiver, TryRecvError};
|
||||||
|
use std::time::{Duration, SystemTime};
|
||||||
use log::{debug, error, info};
|
use log::{debug, error, info};
|
||||||
use crate::config::{load_cdata, NebulaConfig, save_cdata, TFClientConfig};
|
use crate::config::{load_cdata, NebulaConfig, save_cdata, TFClientConfig};
|
||||||
use crate::daemon::ThreadMessageSender;
|
use crate::daemon::ThreadMessageSender;
|
||||||
|
@ -15,13 +16,16 @@ pub enum NebulaWorkerMessage {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn insert_private_key(instance: &str) -> Result<(), Box<dyn Error>> {
|
fn insert_private_key(instance: &str) -> Result<(), Box<dyn Error>> {
|
||||||
|
if !get_nebulaconfig_file(instance).ok_or("Could not get config file location")?.exists() {
|
||||||
|
return Ok(()); // cant insert private key into a file that does not exist - BUT. we can gracefully handle nebula crashing - we cannot gracefully handle this fn failing
|
||||||
|
}
|
||||||
let cdata = load_cdata(instance)?;
|
let cdata = load_cdata(instance)?;
|
||||||
let key = cdata.dh_privkey.ok_or("Missing private key")?;
|
let key = cdata.dh_privkey.ok_or("Missing private key")?;
|
||||||
|
|
||||||
let config_str = fs::read_to_string(get_nebulaconfig_file(instance).ok_or("Could not get config file location")?)?;
|
let config_str = fs::read_to_string(get_nebulaconfig_file(instance).ok_or("Could not get config file location")?)?;
|
||||||
let mut config: NebulaConfig = serde_yaml::from_str(&config_str)?;
|
let mut config: NebulaConfig = serde_yaml::from_str(&config_str)?;
|
||||||
|
|
||||||
config.pki.key = String::from_utf8(key)?;
|
config.pki.key = Some(String::from_utf8(key)?);
|
||||||
|
|
||||||
debug!("inserted private key into config: {:?}", config);
|
debug!("inserted private key into config: {:?}", config);
|
||||||
|
|
||||||
|
@ -63,8 +67,25 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter
|
||||||
};
|
};
|
||||||
info!("nebula process started");
|
info!("nebula process started");
|
||||||
|
|
||||||
|
let mut last_restart_time = SystemTime::now();
|
||||||
|
|
||||||
// dont need to save it, because we do not, in any circumstance, write to it
|
// dont need to save it, because we do not, in any circumstance, write to it
|
||||||
loop {
|
loop {
|
||||||
|
if let Ok(e) = child.try_wait() {
|
||||||
|
if e.is_some() && SystemTime::now() > last_restart_time + Duration::from_secs(5) {
|
||||||
|
info!("nebula process has exited, restarting");
|
||||||
|
child = match run_embedded_nebula(&["-config".to_string(), get_nebulaconfig_file(&instance).unwrap().to_str().unwrap().to_string()]) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => {
|
||||||
|
error!("unable to start embedded nebula binary: {}", e);
|
||||||
|
error!("nebula thread exiting with error");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
info!("nebula process started");
|
||||||
|
last_restart_time = SystemTime::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
match rx.try_recv() {
|
match rx.try_recv() {
|
||||||
Ok(msg) => {
|
Ok(msg) => {
|
||||||
match msg {
|
match msg {
|
||||||
|
@ -113,6 +134,7 @@ pub fn nebulaworker_main(_config: TFClientConfig, instance: String, _transmitter
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
last_restart_time = SystemTime::now();
|
||||||
debug!("nebula process restarted");
|
debug!("nebula process restarted");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
[package]
|
[package]
|
||||||
name = "trifid-pki"
|
name = "trifid-pki"
|
||||||
version = "0.1.8"
|
version = "0.1.9"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
description = "A rust implementation of the Nebula PKI system"
|
description = "A rust implementation of the Nebula PKI system"
|
||||||
license = "AGPL-3.0-or-later"
|
license = "AGPL-3.0-or-later"
|
||||||
|
|
|
@ -307,8 +307,8 @@ pub fn deserialize_ed25519_public(bytes: &[u8]) -> Result<Vec<u8>, Box<dyn Error
|
||||||
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
|
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
|
||||||
return Err(KeyError::WrongPemTag.into())
|
return Err(KeyError::WrongPemTag.into())
|
||||||
}
|
}
|
||||||
if pem.contents.len() != 64 {
|
if pem.contents.len() != 32 {
|
||||||
return Err(KeyError::Not64Bytes.into())
|
return Err(KeyError::Not32Bytes.into())
|
||||||
}
|
}
|
||||||
Ok(pem.contents)
|
Ok(pem.contents)
|
||||||
}
|
}
|
||||||
|
@ -324,8 +324,8 @@ pub fn deserialize_ed25519_public_many(bytes: &[u8]) -> Result<Vec<Vec<u8>>, Box
|
||||||
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
|
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
|
||||||
return Err(KeyError::WrongPemTag.into())
|
return Err(KeyError::WrongPemTag.into())
|
||||||
}
|
}
|
||||||
if pem.contents.len() != 64 {
|
if pem.contents.len() != 32 {
|
||||||
return Err(KeyError::Not64Bytes.into())
|
return Err(KeyError::Not32Bytes.into())
|
||||||
}
|
}
|
||||||
keys.push(pem.contents);
|
keys.push(pem.contents);
|
||||||
}
|
}
|
||||||
|
|
|
@ -296,19 +296,20 @@ fn x25519_serialization() {
|
||||||
#[test]
|
#[test]
|
||||||
fn ed25519_serialization() {
|
fn ed25519_serialization() {
|
||||||
let bytes = [0u8; 64];
|
let bytes = [0u8; 64];
|
||||||
|
let bytes2 = [0u8; 32];
|
||||||
assert_eq!(deserialize_ed25519_private(&serialize_ed25519_private(&bytes)).unwrap(), bytes);
|
assert_eq!(deserialize_ed25519_private(&serialize_ed25519_private(&bytes)).unwrap(), bytes);
|
||||||
assert!(deserialize_ed25519_private(&[0u8; 32]).is_err());
|
assert!(deserialize_ed25519_private(&[0u8; 32]).is_err());
|
||||||
assert_eq!(deserialize_ed25519_public(&serialize_ed25519_public(&bytes)).unwrap(), bytes);
|
assert_eq!(deserialize_ed25519_public(&serialize_ed25519_public(&bytes2)).unwrap(), bytes2);
|
||||||
assert!(deserialize_ed25519_public(&[0u8; 32]).is_err());
|
assert!(deserialize_ed25519_public(&[0u8; 64]).is_err());
|
||||||
|
|
||||||
let mut bytes = vec![];
|
let mut bytes = vec![];
|
||||||
bytes.append(&mut serialize_ed25519_public(&[0u8; 64]));
|
bytes.append(&mut serialize_ed25519_public(&[0u8; 32]));
|
||||||
bytes.append(&mut serialize_ed25519_public(&[1u8; 64]));
|
bytes.append(&mut serialize_ed25519_public(&[1u8; 32]));
|
||||||
let deser = deserialize_ed25519_public_many(&bytes).unwrap();
|
let deser = deserialize_ed25519_public_many(&bytes).unwrap();
|
||||||
assert_eq!(deser[0], [0u8; 64]);
|
assert_eq!(deser[0], [0u8; 32]);
|
||||||
assert_eq!(deser[1], [1u8; 64]);
|
assert_eq!(deser[1], [1u8; 32]);
|
||||||
|
|
||||||
bytes.append(&mut serialize_ed25519_public(&[1u8; 63]));
|
bytes.append(&mut serialize_ed25519_public(&[1u8; 33]));
|
||||||
deserialize_ed25519_public_many(&bytes).unwrap_err();
|
deserialize_ed25519_public_many(&bytes).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -647,11 +648,11 @@ bzBEr00kERQxxTzTsH8cpYEgRoipvmExvg8WP8NdAJEYJosB
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_deserialize_ed25519_public() {
|
fn test_deserialize_ed25519_public() {
|
||||||
let priv_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
|
let pub_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
-----END NEBULA ED25519 PUBLIC KEY-----";
|
-----END NEBULA ED25519 PUBLIC KEY-----";
|
||||||
let short_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
|
let short_key = b"-----BEGIN NEBULA ED25519 PUBLIC KEY-----
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
|
||||||
-----END NEBULA ED25519 PUBLIC KEY-----";
|
-----END NEBULA ED25519 PUBLIC KEY-----";
|
||||||
let invalid_banner = b"-----BEGIN NOT A NEBULA PUBLIC KEY-----
|
let invalid_banner = b"-----BEGIN NOT A NEBULA PUBLIC KEY-----
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||||
|
@ -660,7 +661,7 @@ AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==
|
||||||
-END NEBULA ED25519 PUBLIC KEY-----";
|
-END NEBULA ED25519 PUBLIC KEY-----";
|
||||||
|
|
||||||
deserialize_ed25519_public(priv_key).unwrap();
|
deserialize_ed25519_public(pub_key).unwrap();
|
||||||
|
|
||||||
deserialize_ed25519_public(short_key).unwrap_err();
|
deserialize_ed25519_public(short_key).unwrap_err();
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue