trifid/dnapi-rs/src/message.rs

224 lines
8.9 KiB
Rust
Raw Normal View History

2023-03-29 15:18:33 +00:00
//! Models for interacting with the Defined Networking API.
use base64_serde::base64_serde_type;
2023-05-14 17:47:49 +00:00
use serde::{Deserialize, Serialize};
use serde_with::serde_as;
2023-03-29 15:18:33 +00:00
/// The version 1 `DNClient` API endpoint
2023-03-29 18:31:07 +00:00
pub const ENDPOINT_V1: &str = "/v1/dnclient";
2023-03-29 15:18:33 +00:00
2023-03-29 21:49:02 +00:00
/// The `CheckForUpdate` message type
2023-03-29 21:42:16 +00:00
pub const CHECK_FOR_UPDATE: &str = "CheckForUpdate";
2023-03-29 21:49:02 +00:00
/// The `DoUpdate` message type
2023-03-29 21:42:16 +00:00
pub const DO_UPDATE: &str = "DoUpdate";
2023-03-29 15:18:33 +00:00
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,
2023-03-30 16:13:29 +00:00
/// A base64-encoded message. This must be previously base64-encoded, as the signature is signed over the base64-encoded data.
pub message: String,
2023-03-29 15:18:33 +00:00
#[serde(with = "Base64Standard")]
/// An ed25519 signature over the `message`, which can be verified with the host's previously enrolled ed25519 public key
2023-05-14 17:47:49 +00:00
pub signature: Vec<u8>,
2023-03-29 15:18:33 +00:00
}
#[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,
/// A base64-encoded arbitrary message, the type of which is stated in `message_type`
#[serde(with = "b64_as")]
2023-03-29 15:18:33 +00:00
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`
2023-05-14 17:47:49 +00:00
pub timestamp: String,
2023-03-29 15:18:33 +00:00
}
#[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
2023-05-14 17:47:49 +00:00
pub data: SignedResponse,
2023-03-29 15:18:33 +00:00
}
#[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`
2023-05-14 17:47:49 +00:00
pub signature: Vec<u8>,
2023-03-29 15:18:33 +00:00
}
#[derive(Serialize, Deserialize)]
/// `CheckForUpdateResponseWrapper` contains a response to `CheckForUpdate` inside "data."
pub struct CheckForUpdateResponseWrapper {
/// The response data contained in this message
2023-05-14 17:47:49 +00:00
pub data: CheckForUpdateResponse,
2023-03-29 15:18:33 +00:00
}
#[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
2023-05-14 17:47:49 +00:00
pub update_available: bool,
2023-03-29 15:18:33 +00:00
}
#[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
2023-05-14 17:47:49 +00:00
pub nonce: Vec<u8>,
2023-03-29 15:18:33 +00:00
}
#[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.
2023-05-14 17:47:49 +00:00
pub trusted_keys: Vec<u8>,
2023-03-29 15:18:33 +00:00
}
/// The REST enrollment endpoint
2023-03-29 18:31:07 +00:00
pub const ENROLL_ENDPOINT: &str = "/v2/enroll";
2023-03-29 15:18:33 +00:00
2023-05-14 17:47:49 +00:00
#[derive(Serialize, Deserialize, Debug)]
2023-03-29 15:18:33 +00:00
/// `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`
2023-05-14 17:47:49 +00:00
pub timestamp: String,
2023-03-29 15:18:33 +00:00
}
#[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
2023-05-14 17:47:49 +00:00
data: EnrollResponseData,
2023-03-29 15:18:33 +00:00
},
/// 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>`
2023-05-14 17:47:49 +00:00
errors: APIErrors,
},
2023-03-29 15:18:33 +00:00
}
#[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
2023-05-14 17:47:49 +00:00
pub organization: EnrollResponseDataOrg,
2023-03-29 15:18:33 +00:00
}
#[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
2023-05-14 17:47:49 +00:00
pub name: String,
2023-03-29 15:18:33 +00:00
}
#[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
2023-05-14 17:47:49 +00:00
pub path: Option<String>,
2023-03-29 15:18:33 +00:00
}
/// A type alias to a array of `APIErrors`. Just for parity with dnapi.
2023-05-14 17:47:49 +00:00
pub type APIErrors = Vec<APIError>;
mod b64_as {
use serde::{Serialize, Deserialize};
use serde::{Deserializer, Serializer};
use base64::Engine;
pub fn serialize<S: Serializer>(v: &Vec<u8>, s: S) -> Result<S::Ok, S::Error> {
let base64 = base64::engine::general_purpose::STANDARD.encode(v);
<String>::serialize(&base64, s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let base64 = <Option<String>>::deserialize(d)?;
match base64 {
Some(v) => {
base64::engine::general_purpose::STANDARD.decode(v.as_bytes())
.map(|v| v)
.map_err(|e| serde::de::Error::custom(e))
},
None => Ok(vec![]),
}
}
}