diff --git a/docs/api.yaml b/docs/api.yaml index d1544c3..29287a5 100644 --- a/docs/api.yaml +++ b/docs/api.yaml @@ -118,7 +118,7 @@ components: description: Sender overlay IP address type: string format: ipv6 - example: 34f:b680:ba6e:7ced:355f:346f:d97b:eecb + example: 249:abcd:0123:defa::1 src_pk: description: Sender public key, hex encoded type: string @@ -147,16 +147,32 @@ components: type: object properties: dst: - description: An IP in the subnet of the receiver node - type: string - format: ipv6 - example: 249:abcd:0123:defa::1 + $ref: '#/components/schemas/MessageDestination' payload: description: The message to send, base64 encoded type: string format: byte example: xuV+ + MessageDestination: + oneOf: + - description: An IP in the subnet of the receiver node + type: object + properties: + ip: + description: The target IP of the message + format: ipv6 + example: 249:abcd:0123:defa::1 + - description: The hex encoded public key of the receiver node + type: object + properties: + pk: + description: The hex encoded public key of the target node + type: string + minLength: 64 + maxLength: 64 + example: bb39b4a3a4efd70f3e05e37887677e02efbda14681d0acd3882bc0f754792c32 + PushMessageResponse: description: The ID generated for a message after pushing it to the system type: object diff --git a/src/api.rs b/src/api.rs index 1e8b48a..6ba4f2b 100644 --- a/src/api.rs +++ b/src/api.rs @@ -36,11 +36,18 @@ struct HttpServerState { #[derive(Debug, Deserialize)] struct MessageSendInfo { - dst: IpAddr, + dst: MessageDestination, #[serde(with = "base64")] payload: Vec, } +#[derive(Debug, Deserialize)] +#[serde(rename_all = "lowercase")] +enum MessageDestination { + Ip(IpAddr), + Pk(PublicKey), +} + #[derive(Serialize)] struct MessageReceiveInfo { id: MessageId, @@ -52,6 +59,16 @@ struct MessageReceiveInfo { payload: Vec, } +impl MessageDestination { + /// Get the IP address of the destination. + fn ip(self) -> IpAddr { + match self { + MessageDestination::Ip(ip) => ip, + MessageDestination::Pk(pk) => IpAddr::V6(pk.address()), + } + } +} + impl Http { /// Spawns a new HTTP API server on the provided listening address. pub fn spawn(message_stack: MessageStack, listen_addr: &SocketAddr) -> Self { @@ -126,17 +143,16 @@ async fn push_message( State(state): State, Json(message_info): Json, ) -> Result, StatusCode> { + let dst = message_info.dst.ip(); debug!( - "Pushing new message of {} bytes to message stack for target {}", + "Pushing new message of {} bytes to message stack for target {dst}", message_info.payload.len(), - message_info.dst ); - let id = state.message_stack.push_message( - message_info.dst, - message_info.payload, - DEFAULT_MESSAGE_TRY_DURATION, - ); + let id = + state + .message_stack + .push_message(dst, message_info.payload, DEFAULT_MESSAGE_TRY_DURATION); Ok(Json(PushMessageResponse { id })) } diff --git a/src/crypto.rs b/src/crypto.rs index 334257e..2b6c404 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -13,7 +13,7 @@ use std::{ use aes_gcm::{aead::OsRng, AeadCore, AeadInPlace, Aes256Gcm, Key, KeyInit}; use blake2::{Blake2b, Digest}; use digest::consts::U16; -use serde::Serialize; +use serde::{de::Visitor, Deserialize, Serialize}; use tokio::{ fs::File, io::{AsyncReadExt, AsyncWriteExt}, @@ -279,6 +279,38 @@ impl Serialize for PublicKey { } } +struct PublicKeyVisitor; +impl<'de> Visitor<'de> for PublicKeyVisitor { + type Value = PublicKey; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("A hex encoded public key (64 characters)") + } + + fn visit_str(self, v: &str) -> Result + where + E: serde::de::Error, + { + if v.len() != 64 { + Err(E::custom("Public key is 64 characters long")) + } else { + let mut backing = [0; 32]; + faster_hex::hex_decode(v.as_bytes(), &mut backing) + .map_err(|_| E::custom("PublicKey is not valid hex"))?; + Ok(PublicKey(backing.into())) + } + } +} + +impl<'de> Deserialize<'de> for PublicKey { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + deserializer.deserialize_str(PublicKeyVisitor) + } +} + impl From<[u8; 32]> for PublicKey { /// Given a byte array, construct a `PublicKey`. fn from(bytes: [u8; 32]) -> PublicKey {