some work
This commit is contained in:
parent
b6940ba0f3
commit
553a95d6bc
|
@ -619,6 +619,20 @@ dependencies = [
|
||||||
"windows-sys 0.45.0",
|
"windows-sys 0.45.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dnapi-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.21.0",
|
||||||
|
"base64-serde",
|
||||||
|
"log",
|
||||||
|
"reqwest",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"trifid-pki",
|
||||||
|
"url",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dotenvy"
|
name = "dotenvy"
|
||||||
version = "0.15.6"
|
version = "0.15.6"
|
||||||
|
@ -2061,18 +2075,18 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.158"
|
version = "1.0.159"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9"
|
checksum = "3c04e8343c3daeec41f58990b9d77068df31209f2af111e059e9fe9646693065"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"serde_derive",
|
"serde_derive",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_derive"
|
name = "serde_derive"
|
||||||
version = "1.0.158"
|
version = "1.0.159"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
|
checksum = "4c614d17805b093df4b147b51339e7e44bf05ef59fba1e45d83500bcfb4d8585"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
|
@ -2081,9 +2095,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.94"
|
version = "1.0.95"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea"
|
checksum = "d721eca97ac802aa7777b701877c8004d950fc142651367300d21c1cc0194744"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"ryu",
|
"ryu",
|
||||||
|
@ -2436,6 +2450,7 @@ dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"ctrlc",
|
"ctrlc",
|
||||||
"dirs 5.0.0",
|
"dirs 5.0.0",
|
||||||
|
"dnapi-rs",
|
||||||
"flate2",
|
"flate2",
|
||||||
"hex",
|
"hex",
|
||||||
"ipnet",
|
"ipnet",
|
||||||
|
|
|
@ -2,5 +2,6 @@
|
||||||
members = [
|
members = [
|
||||||
"trifid-api",
|
"trifid-api",
|
||||||
"tfclient",
|
"tfclient",
|
||||||
"trifid-pki"
|
"trifid-pki",
|
||||||
|
"dnapi-rs"
|
||||||
]
|
]
|
|
@ -0,0 +1,22 @@
|
||||||
|
[package]
|
||||||
|
name = "dnapi-rs"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
description = "A rust client for the Defined Networking API"
|
||||||
|
license = "AGPL-3.0-or-later"
|
||||||
|
documentation = "https://docs.rs/dnapi-rs"
|
||||||
|
homepage = "https://git.e3t.cc/~core/trifid"
|
||||||
|
repository = "https://git.e3t.cc/~core/trifid"
|
||||||
|
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
serde = { version = "1.0.159", features = ["derive"] }
|
||||||
|
base64-serde = "0.7.0"
|
||||||
|
log = "0.4.17"
|
||||||
|
reqwest = { version = "0.11.16", features = ["blocking", "json"] }
|
||||||
|
url = "2.3.1"
|
||||||
|
base64 = "0.21.0"
|
||||||
|
serde_json = "1.0.95"
|
||||||
|
trifid-pki = { version = "0.1.6", path = "../trifid-pki" }
|
|
@ -0,0 +1 @@
|
||||||
|
//! Client structs to handle communication with the Defined Networking API.
|
|
@ -0,0 +1,38 @@
|
||||||
|
//! Contains the `Credentials` struct, which contains all keys, IDs, organizations and other identity-related and security-related data that is persistent in a `Client`
|
||||||
|
|
||||||
|
use std::error::Error;
|
||||||
|
use trifid_pki::cert::{deserialize_ed25519_public, serialize_ed25519_public};
|
||||||
|
use trifid_pki::ed25519_dalek::{SigningKey, VerifyingKey};
|
||||||
|
|
||||||
|
/// Contains information necessary to make requests against the `DNClient` API.
|
||||||
|
pub struct Credentials {
|
||||||
|
/// The assigned Host ID that this client represents
|
||||||
|
pub host_id: String,
|
||||||
|
/// The ed25519 private key used to sign requests against the API
|
||||||
|
pub ed_privkey: SigningKey,
|
||||||
|
/// The counter used in the other API requests. It is unknown what the purpose of this is, but the original client persists it and it is needed for API calls.
|
||||||
|
pub counter: u32,
|
||||||
|
/// The set of trusted ed25519 keys that may be used by the API to sign API responses.
|
||||||
|
pub trusted_keys: Vec<VerifyingKey>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Converts an array of `VerifyingKey`s to a singular bundle of PEM-encoded keys
|
||||||
|
pub fn ed25519_public_keys_to_pem(keys: &[VerifyingKey]) -> Vec<u8> {
|
||||||
|
let mut res = vec![];
|
||||||
|
|
||||||
|
for key in keys {
|
||||||
|
res.append(&mut serialize_ed25519_public(&key.to_bytes()));
|
||||||
|
}
|
||||||
|
|
||||||
|
res
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn ed25519_public_keys_from_pem(pem: Vec<u8>) -> Result<Vec<VerifyingKey>, Box<dyn Error>> {
|
||||||
|
let mut keys = vec![];
|
||||||
|
|
||||||
|
for key in keys.chunks(32) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(keys)
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
//! # dnapi-rs
|
||||||
|
//! **dnapi-rs** is a Rust-native crate for interacting with the Defined Networking client API. It is a direct port of `dnapi`, an officially maintained API client by Defined Networking.
|
||||||
|
//!
|
||||||
|
//! This crate is maintained as a part of the trifid project. Check out the other crates in [the git repository](https://git.e3t.cc/~core/trifid).
|
||||||
|
|
||||||
|
#![warn(clippy::pedantic)]
|
||||||
|
#![warn(clippy::nursery)]
|
||||||
|
#![deny(clippy::unwrap_used)]
|
||||||
|
#![deny(clippy::expect_used)]
|
||||||
|
#![deny(missing_docs)]
|
||||||
|
#![deny(clippy::missing_errors_doc)]
|
||||||
|
#![deny(clippy::missing_panics_doc)]
|
||||||
|
#![deny(clippy::missing_safety_doc)]
|
||||||
|
#![allow(clippy::must_use_candidate)]
|
||||||
|
#![allow(clippy::too_many_lines)]
|
||||||
|
#![allow(clippy::module_name_repetitions)]
|
||||||
|
|
||||||
|
pub mod message;
|
||||||
|
pub mod client;
|
||||||
|
pub mod credentials;
|
|
@ -0,0 +1,197 @@
|
||||||
|
//! Models for interacting with the Defined Networking API.
|
||||||
|
|
||||||
|
use base64_serde::base64_serde_type;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
/// The version 1 `DNClient` API endpoint
|
||||||
|
const ENDPOINT_V1: &str = "/v1/dnclient";
|
||||||
|
|
||||||
|
base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `RequestV1` is the version 1 `DNClient` request message.
|
||||||
|
pub struct RequestV1 {
|
||||||
|
/// Version is always 1
|
||||||
|
pub version: i32,
|
||||||
|
#[serde(rename = "hostID")]
|
||||||
|
/// The Host ID of this dnclient instance
|
||||||
|
pub host_id: String,
|
||||||
|
/// The counter last returned by the server
|
||||||
|
pub counter: u32,
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// A base64-encoded message
|
||||||
|
pub message: Vec<u8>,
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key
|
||||||
|
pub signature: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `RequestWrapper` wraps a `DNClient` request message. It consists of a
|
||||||
|
/// type and value, with the type string indicating how to interpret the value blob.
|
||||||
|
pub struct RequestWrapper {
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
/// The type of the message. Used to determine how `value` is encoded
|
||||||
|
pub message_type: String,
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// A base64-encoded arbitrary message, the type of which is stated in `message_type`
|
||||||
|
pub value: Vec<u8>,
|
||||||
|
/// The timestamp of when this message was sent. Follows the format `%Y-%m-%dT%H:%M:%S.%f%:z`, or:
|
||||||
|
/// <4-digit year>-<two-digit-month>-<two-digit-day>T<two-digit-hour, 24-hour>:<two-digit-minute>:<two-digit-second>.<nanoseconds, zero-padded><offset with semicolon>
|
||||||
|
/// For example:
|
||||||
|
/// `2023-03-29T09:56:42.380006369-04:00`
|
||||||
|
/// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4`
|
||||||
|
pub timestamp: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `SignedResponseWrapper` contains a response message and a signature to validate inside `data`.
|
||||||
|
pub struct SignedResponseWrapper {
|
||||||
|
/// The response data contained in this message
|
||||||
|
pub data: SignedResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `SignedResponse` contains a response message and a signature to validate.
|
||||||
|
pub struct SignedResponse {
|
||||||
|
/// The API version - always 1
|
||||||
|
pub version: i32,
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The Base64-encoded message signed inside this message
|
||||||
|
pub message: Vec<u8>,
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The ed25519 signature over the `message`
|
||||||
|
pub signature: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `CheckForUpdateResponseWrapper` contains a response to `CheckForUpdate` inside "data."
|
||||||
|
pub struct CheckForUpdateResponseWrapper {
|
||||||
|
/// The response data contained in this message
|
||||||
|
pub data: CheckForUpdateResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `CheckForUpdateResponse` is the response generated for a `CheckForUpdate` request.
|
||||||
|
pub struct CheckForUpdateResponse {
|
||||||
|
#[serde(rename = "updateAvailable")]
|
||||||
|
/// Set to true if a config update is available
|
||||||
|
pub update_available: bool
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `DoUpdateRequest` is the request sent for a `DoUpdate` request.
|
||||||
|
pub struct DoUpdateRequest {
|
||||||
|
#[serde(rename = "edPubkeyPEM")]
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The new ed25519 public key that should be used for future API requests
|
||||||
|
pub ed_pubkey_pem: Vec<u8>,
|
||||||
|
#[serde(rename = "dhPubkeyPEM")]
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The new ECDH public key that the Nebula certificate should be signed for
|
||||||
|
pub dh_pubkey_pem: Vec<u8>,
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// A randomized value used to uniquely identify this request.
|
||||||
|
/// The original client uses a randomized, 16-byte value here, which dnapi-rs replicates
|
||||||
|
pub nonce: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// A server response to a `DoUpdateRequest`, with the updated config and key information
|
||||||
|
pub struct DoUpdateResponse {
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The base64-encoded Nebula config. It does **NOT** have a private-key, which must be inserted explicitly before Nebula can be ran
|
||||||
|
pub config: Vec<u8>,
|
||||||
|
/// The new config counter. It is unknown what the purpose of this is, but the original client keeps track of it and it is used later in the api
|
||||||
|
pub counter: u32,
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The same base64-encoded nonce that was sent in the `DoUpdateRequest`.
|
||||||
|
pub nonce: Vec<u8>,
|
||||||
|
#[serde(rename = "trustedKeys")]
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// A new set of trusted ed25519 keys that can be used by the server to sign messages.
|
||||||
|
pub trusted_keys: Vec<u8>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The REST enrollment endpoint
|
||||||
|
const ENROLL_ENDPOINT: &str = "/v2/enroll";
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `EnrollRequest` is issued to the `ENROLL_ENDPOINT` to enroll this `dnclient` with a dnapi organization
|
||||||
|
pub struct EnrollRequest {
|
||||||
|
/// The enrollment code given by the API server.
|
||||||
|
pub code: String,
|
||||||
|
#[serde(rename = "dhPubkey")]
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The ECDH public-key that should be used to sign the Nebula certificate given to this node.
|
||||||
|
pub dh_pubkey: Vec<u8>,
|
||||||
|
#[serde(rename = "edPubkey")]
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The Ed25519 public-key that this node will use to sign messages sent to the API.
|
||||||
|
pub ed_pubkey: Vec<u8>,
|
||||||
|
/// The timestamp of when this request was sent. Follows the format `%Y-%m-%dT%H:%M:%S.%f%:z`, or:
|
||||||
|
/// <4-digit year>-<two-digit-month>-<two-digit-day>T<two-digit-hour, 24-hour>:<two-digit-minute>:<two-digit-second>.<nanoseconds, zero-padded><offset with semicolon>
|
||||||
|
/// For example:
|
||||||
|
/// `2023-03-29T09:56:42.380006369-04:00`
|
||||||
|
/// would represent `29 March 03, 2023, 09:56:42.380006369 UTC-4`
|
||||||
|
pub timestamp: String
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
/// The response to an `EnrollRequest`
|
||||||
|
pub enum EnrollResponse {
|
||||||
|
/// A successful enrollment, with a `data` field pointing to an `EnrollResponseData`
|
||||||
|
Success {
|
||||||
|
/// The response data from this response
|
||||||
|
data: EnrollResponseData
|
||||||
|
},
|
||||||
|
/// An unsuccessful enrollment, with an `errors` field pointing to an array of `APIError`s.
|
||||||
|
Error {
|
||||||
|
/// A list of `APIError`s that happened while trying to enroll. `APIErrors` is a type alias to `Vec<APIError>`
|
||||||
|
errors: APIErrors
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// The data included in an successful enrollment.
|
||||||
|
pub struct EnrollResponseData {
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// The base64-encoded Nebula config. It does **NOT** have a private-key, which must be inserted explicitly before Nebula can be ran
|
||||||
|
pub config: Vec<u8>,
|
||||||
|
#[serde(rename = "hostID")]
|
||||||
|
/// The server-side Host ID that this node now has.
|
||||||
|
pub host_id: String,
|
||||||
|
/// The new config counter. It is unknown what the purpose of this is, but the original client keeps track of it and it is used later in the api
|
||||||
|
pub counter: u32,
|
||||||
|
#[serde(rename = "trustedKeys")]
|
||||||
|
#[serde(with = "Base64Standard")]
|
||||||
|
/// A new set of trusted ed25519 keys that can be used by the server to sign messages.
|
||||||
|
pub trusted_keys: Vec<u8>,
|
||||||
|
/// The organization data that this node is now a part of
|
||||||
|
pub organization: EnrollResponseDataOrg
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// The organization data that this node is now a part of
|
||||||
|
pub struct EnrollResponseDataOrg {
|
||||||
|
/// The organization ID that this node is now a part of
|
||||||
|
pub id: String,
|
||||||
|
/// The name of the organization that this node is now a part of
|
||||||
|
pub name: String
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize)]
|
||||||
|
/// `APIError` represents a single error returned in an API error response.
|
||||||
|
pub struct APIError {
|
||||||
|
/// The error code
|
||||||
|
pub code: String,
|
||||||
|
/// The human-readable error message
|
||||||
|
pub message: String,
|
||||||
|
/// An optional path to where the error occured
|
||||||
|
pub path: Option<String>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A type alias to a array of `APIErrors`. Just for parity with dnapi.
|
||||||
|
pub type APIErrors = Vec<APIError>;
|
|
@ -24,6 +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.0", path = "../dnapi-rs" }
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
serde = { version = "1.0.157", features = ["derive"] }
|
serde = { version = "1.0.157", features = ["derive"] }
|
||||||
|
|
|
@ -1,145 +0,0 @@
|
||||||
use std::error::Error;
|
|
||||||
use base64_serde::base64_serde_type;
|
|
||||||
use log::trace;
|
|
||||||
use reqwest::blocking::Client;
|
|
||||||
use serde::{Serialize, Deserialize};
|
|
||||||
use url::Url;
|
|
||||||
|
|
||||||
const ENDPOINT_V1: &str = "/v1/dnclient";
|
|
||||||
|
|
||||||
base64_serde_type!(Base64Standard, base64::engine::general_purpose::STANDARD);
|
|
||||||
|
|
||||||
pub fn enroll(server: &Url, request: &EnrollRequest) -> Result<APIResponse, Box<dyn Error>> {
|
|
||||||
let endpoint = server.join("/v2/enroll")?;
|
|
||||||
let client = Client::new();
|
|
||||||
|
|
||||||
let text = serde_json::to_string(request)?;
|
|
||||||
|
|
||||||
trace!("sending enroll: {}", text);
|
|
||||||
|
|
||||||
let resp = client.post(endpoint).body(text).send()?;
|
|
||||||
Ok(resp.json()?)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct RequestV1 {
|
|
||||||
pub version: i32,
|
|
||||||
#[serde(rename = "hostID")]
|
|
||||||
pub host_id: String,
|
|
||||||
pub counter: u32,
|
|
||||||
pub message: String,
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub signature: Vec<u8>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct RequestWrapper {
|
|
||||||
#[serde(rename = "type")]
|
|
||||||
pub message_type: String,
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub value: Vec<u8>,
|
|
||||||
pub timestamp: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct SignedResponseWrapper {
|
|
||||||
pub data: SignedResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct SignedResponse {
|
|
||||||
pub version: i32,
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub message: Vec<u8>,
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub signature: Vec<u8>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct CheckForUpdateResponseWrapper {
|
|
||||||
pub data: CheckForUpdateResponse
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct CheckForUpdateResponse {
|
|
||||||
#[serde(rename = "updateAvailable")]
|
|
||||||
pub update_available: bool
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct DoUpdateRequest {
|
|
||||||
#[serde(rename = "edPubkeyPEM")]
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub ed_pubkey_pem: Vec<u8>,
|
|
||||||
#[serde(rename = "dhPubkeyPEM")]
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub dh_pubkey_pem: Vec<u8>,
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub nonce: Vec<u8>
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct DoUpdateResponse {
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub config: Vec<u8>,
|
|
||||||
pub counter: u32,
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub nonce: Vec<u8>,
|
|
||||||
#[serde(rename = "trustedKeys")]
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub trusted_keys: Vec<u8>
|
|
||||||
}
|
|
||||||
|
|
||||||
const ENROLL_ENDPOINT: &str = "/v2/enroll";
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct EnrollRequest {
|
|
||||||
pub code: String,
|
|
||||||
#[serde(rename = "dhPubkey")]
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub dh_pubkey: Vec<u8>,
|
|
||||||
#[serde(rename = "edPubkey")]
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub ed_pubkey: Vec<u8>,
|
|
||||||
pub timestamp: String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
#[serde(untagged)]
|
|
||||||
pub enum EnrollResponse {
|
|
||||||
Success {
|
|
||||||
data: EnrollResponseData
|
|
||||||
},
|
|
||||||
Error {
|
|
||||||
errors: APIErrors
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct EnrollResponseData {
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub config: Vec<u8>,
|
|
||||||
#[serde(rename = "hostID")]
|
|
||||||
pub host_id: String,
|
|
||||||
pub counter: u32,
|
|
||||||
#[serde(rename = "trustedKeys")]
|
|
||||||
#[serde(with = "Base64Standard")]
|
|
||||||
pub trusted_keys: Vec<u8>,
|
|
||||||
pub organization: EnrollResponseDataOrg
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct EnrollResponseDataOrg {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
|
||||||
pub struct APIError {
|
|
||||||
pub code: String,
|
|
||||||
pub message: String,
|
|
||||||
pub path: Option<String>
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type APIErrors = Vec<APIError>;
|
|
|
@ -8,7 +8,7 @@ use trifid_pki::cert::{serialize_ed25519_public, serialize_x25519_public};
|
||||||
use trifid_pki::ed25519_dalek::{SecretKey, SigningKey};
|
use trifid_pki::ed25519_dalek::{SecretKey, SigningKey};
|
||||||
use trifid_pki::rand_core::OsRng;
|
use trifid_pki::rand_core::OsRng;
|
||||||
use trifid_pki::x25519_dalek::StaticSecret;
|
use trifid_pki::x25519_dalek::StaticSecret;
|
||||||
use crate::api::{APIResponse, enroll, EnrollRequest};
|
use crate::message::{APIResponse, enroll, EnrollRequest};
|
||||||
use crate::config::{load_cdata, save_cdata, TFClientConfig};
|
use crate::config::{load_cdata, save_cdata, TFClientConfig};
|
||||||
use crate::daemon::ThreadMessageSender;
|
use crate::daemon::ThreadMessageSender;
|
||||||
|
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
/// Essentially a direct port of https://github.com/DefinedNet/dnapi
|
|
|
@ -23,7 +23,6 @@ pub mod config;
|
||||||
pub mod service;
|
pub mod service;
|
||||||
pub mod apiworker;
|
pub mod apiworker;
|
||||||
pub mod socketworker;
|
pub mod socketworker;
|
||||||
pub mod api;
|
|
||||||
pub mod socketclient;
|
pub mod socketclient;
|
||||||
pub mod timerworker;
|
pub mod timerworker;
|
||||||
|
|
||||||
|
|
|
@ -313,6 +313,26 @@ pub fn deserialize_ed25519_public(bytes: &[u8]) -> Result<Vec<u8>, Box<dyn Error
|
||||||
Ok(pem.contents)
|
Ok(pem.contents)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Attempt to deserialize multiple PEM encoded Ed25519 public keys
|
||||||
|
/// # Errors
|
||||||
|
/// This function will return an error if the PEM data is invalid or has the wrong tag
|
||||||
|
pub fn deserialize_ed25519_public_many(bytes: &[u8]) -> Result<Vec<Vec<u8>>, Box<dyn Error>> {
|
||||||
|
let mut keys = vec![];
|
||||||
|
let pems = pem::parse_many(bytes)?;
|
||||||
|
|
||||||
|
for pem in pems {
|
||||||
|
if pem.tag != ED25519_PUBLIC_KEY_BANNER {
|
||||||
|
return Err(KeyError::WrongPemTag.into())
|
||||||
|
}
|
||||||
|
if pem.contents.len() != 64 {
|
||||||
|
return Err(KeyError::Not64Bytes.into())
|
||||||
|
}
|
||||||
|
keys.push(pem.contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(keys)
|
||||||
|
}
|
||||||
|
|
||||||
impl NebulaCertificate {
|
impl NebulaCertificate {
|
||||||
/// Sign a nebula certificate with the provided private key
|
/// Sign a nebula certificate with the provided private key
|
||||||
/// # Errors
|
/// # Errors
|
||||||
|
|
|
@ -6,7 +6,7 @@ use std::net::Ipv4Addr;
|
||||||
use std::ops::{Add, Sub};
|
use std::ops::{Add, Sub};
|
||||||
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
|
use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH};
|
||||||
use ipnet::Ipv4Net;
|
use ipnet::Ipv4Net;
|
||||||
use crate::cert::{CertificateValidity, deserialize_ed25519_private, deserialize_ed25519_public, deserialize_nebula_certificate, deserialize_nebula_certificate_from_pem, deserialize_x25519_private, deserialize_x25519_public, NebulaCertificate, NebulaCertificateDetails, serialize_ed25519_private, serialize_ed25519_public, serialize_x25519_private, serialize_x25519_public};
|
use crate::cert::{CertificateValidity, deserialize_ed25519_private, deserialize_ed25519_public, deserialize_ed25519_public_many, deserialize_nebula_certificate, deserialize_nebula_certificate_from_pem, deserialize_x25519_private, deserialize_x25519_public, NebulaCertificate, NebulaCertificateDetails, serialize_ed25519_private, serialize_ed25519_public, serialize_x25519_private, serialize_x25519_public};
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use ed25519_dalek::{SigningKey, VerifyingKey};
|
use ed25519_dalek::{SigningKey, VerifyingKey};
|
||||||
use quick_protobuf::{MessageWrite, Writer};
|
use quick_protobuf::{MessageWrite, Writer};
|
||||||
|
@ -300,6 +300,16 @@ fn ed25519_serialization() {
|
||||||
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(&bytes)).unwrap(), bytes);
|
||||||
assert!(deserialize_ed25519_public(&[0u8; 32]).is_err());
|
assert!(deserialize_ed25519_public(&[0u8; 32]).is_err());
|
||||||
|
|
||||||
|
let mut bytes = vec![];
|
||||||
|
bytes.append(&mut serialize_ed25519_public(&[0u8; 64]));
|
||||||
|
bytes.append(&mut serialize_ed25519_public(&[1u8; 64]));
|
||||||
|
let deser = deserialize_ed25519_public_many(&bytes).unwrap();
|
||||||
|
assert_eq!(deser[0], [0u8; 64]);
|
||||||
|
assert_eq!(deser[1], [1u8; 64]);
|
||||||
|
|
||||||
|
bytes.append(&mut serialize_ed25519_public(&[1u8; 63]));
|
||||||
|
deserialize_ed25519_public_many(&bytes).unwrap_err();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|
Loading…
Reference in New Issue