mirror of
https://github.com/Blah-IM/blahrs.git
synced 2025-05-01 08:41:09 +00:00
refactor(types): WithSig
-> Signed
This commit is contained in:
parent
73eb441a26
commit
93d1589730
7 changed files with 39 additions and 39 deletions
|
@ -60,7 +60,7 @@ impl fmt::Display for UserKey {
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
#[serde(deny_unknown_fields)]
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct WithSig<T> {
|
pub struct Signed<T> {
|
||||||
#[serde(with = "hex::serde")]
|
#[serde(with = "hex::serde")]
|
||||||
pub sig: [u8; SIGNATURE_LENGTH],
|
pub sig: [u8; SIGNATURE_LENGTH],
|
||||||
pub signee: Signee<T>,
|
pub signee: Signee<T>,
|
||||||
|
@ -82,7 +82,7 @@ pub fn get_timestamp() -> u64 {
|
||||||
.as_secs()
|
.as_secs()
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: Serialize> WithSig<T> {
|
impl<T: Serialize> Signed<T> {
|
||||||
/// Sign the payload with the given `key`.
|
/// Sign the payload with the given `key`.
|
||||||
pub fn sign(
|
pub fn sign(
|
||||||
key: &SigningKey,
|
key: &SigningKey,
|
||||||
|
@ -308,7 +308,7 @@ impl RichText {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type SignedChatMsg = WithSig<ChatPayload>;
|
pub type SignedChatMsg = Signed<ChatPayload>;
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
pub struct RoomMetadata {
|
pub struct RoomMetadata {
|
||||||
|
@ -551,7 +551,7 @@ mod tests {
|
||||||
let mut fake_rng = rand::rngs::mock::StepRng::new(0x42, 1);
|
let mut fake_rng = rand::rngs::mock::StepRng::new(0x42, 1);
|
||||||
let signing_key = SigningKey::from_bytes(&[0x42; 32]);
|
let signing_key = SigningKey::from_bytes(&[0x42; 32]);
|
||||||
let timestamp = 0xDEAD_BEEF;
|
let timestamp = 0xDEAD_BEEF;
|
||||||
let msg = WithSig::sign(
|
let msg = Signed::sign(
|
||||||
&signing_key,
|
&signing_key,
|
||||||
timestamp,
|
timestamp,
|
||||||
&mut fake_rng,
|
&mut fake_rng,
|
||||||
|
@ -568,7 +568,7 @@ mod tests {
|
||||||
]];
|
]];
|
||||||
expect.assert_eq(&json);
|
expect.assert_eq(&json);
|
||||||
|
|
||||||
let roundtrip_msg = serde_json::from_str::<WithSig<ChatPayload>>(&json).unwrap();
|
let roundtrip_msg = serde_json::from_str::<Signed<ChatPayload>>(&json).unwrap();
|
||||||
assert_eq!(roundtrip_msg, msg);
|
assert_eq!(roundtrip_msg, msg);
|
||||||
roundtrip_msg.verify().unwrap();
|
roundtrip_msg.verify().unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use std::{fs, io};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use blah_types::{
|
use blah_types::{
|
||||||
bitflags, get_timestamp, ChatPayload, CreateGroup, CreateRoomPayload, Id, MemberPermission,
|
bitflags, get_timestamp, ChatPayload, CreateGroup, CreateRoomPayload, Id, MemberPermission,
|
||||||
RichText, RoomAttrs, RoomMember, RoomMemberList, ServerPermission, UserKey, WithSig,
|
RichText, RoomAttrs, RoomMember, RoomMemberList, ServerPermission, Signed, UserKey,
|
||||||
};
|
};
|
||||||
use ed25519_dalek::pkcs8::spki::der::pem::LineEnding;
|
use ed25519_dalek::pkcs8::spki::der::pem::LineEnding;
|
||||||
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
|
use ed25519_dalek::pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey};
|
||||||
|
@ -227,7 +227,7 @@ async fn main_api(api_url: Url, command: ApiCommand) -> Result<()> {
|
||||||
user: UserKey(key.verifying_key().to_bytes()),
|
user: UserKey(key.verifying_key().to_bytes()),
|
||||||
}]),
|
}]),
|
||||||
});
|
});
|
||||||
let payload = WithSig::sign(&key, get_timestamp(), &mut OsRng, payload)?;
|
let payload = Signed::sign(&key, get_timestamp(), &mut OsRng, payload)?;
|
||||||
|
|
||||||
let ret = client
|
let ret = client
|
||||||
.post(api_url.join("/room/create")?)
|
.post(api_url.join("/room/create")?)
|
||||||
|
@ -249,10 +249,10 @@ async fn main_api(api_url: Url, command: ApiCommand) -> Result<()> {
|
||||||
room: Id(room),
|
room: Id(room),
|
||||||
rich_text: RichText::from(text),
|
rich_text: RichText::from(text),
|
||||||
};
|
};
|
||||||
let payload = WithSig::sign(&key, get_timestamp(), &mut OsRng, payload)?;
|
let payload = Signed::sign(&key, get_timestamp(), &mut OsRng, payload)?;
|
||||||
|
|
||||||
let ret = client
|
let ret = client
|
||||||
.post(api_url.join(&format!("/room/{room}/item"))?)
|
.post(api_url.join(&format!("/room/{room}/msg"))?)
|
||||||
.json(&payload)
|
.json(&payload)
|
||||||
.send()
|
.send()
|
||||||
.await?
|
.await?
|
||||||
|
|
|
@ -8,7 +8,7 @@ use std::task::{Context, Poll};
|
||||||
|
|
||||||
use anyhow::{bail, Context as _, Result};
|
use anyhow::{bail, Context as _, Result};
|
||||||
use axum::extract::ws::{Message, WebSocket};
|
use axum::extract::ws::{Message, WebSocket};
|
||||||
use blah_types::{AuthPayload, SignedChatMsg, WithSig};
|
use blah_types::{AuthPayload, Signed, SignedChatMsg};
|
||||||
use futures_util::future::Either;
|
use futures_util::future::Either;
|
||||||
use futures_util::stream::SplitSink;
|
use futures_util::stream::SplitSink;
|
||||||
use futures_util::{stream_select, SinkExt as _, Stream, StreamExt};
|
use futures_util::{stream_select, SinkExt as _, Stream, StreamExt};
|
||||||
|
@ -113,7 +113,7 @@ pub async fn handle_ws(st: Arc<AppState>, ws: &mut WebSocket) -> Result<Infallib
|
||||||
.await
|
.await
|
||||||
.context("authentication timeout")?
|
.context("authentication timeout")?
|
||||||
.ok_or(StreamEnded)??;
|
.ok_or(StreamEnded)??;
|
||||||
let auth = serde_json::from_str::<WithSig<AuthPayload>>(&payload)?;
|
let auth = serde_json::from_str::<Signed<AuthPayload>>(&payload)?;
|
||||||
st.verify_signed_data(&auth)?;
|
st.verify_signed_data(&auth)?;
|
||||||
|
|
||||||
st.db
|
st.db
|
||||||
|
|
|
@ -12,8 +12,8 @@ use axum::{Json, Router};
|
||||||
use axum_extra::extract::WithRejection as R;
|
use axum_extra::extract::WithRejection as R;
|
||||||
use blah_types::{
|
use blah_types::{
|
||||||
ChatPayload, CreateGroup, CreatePeerChat, CreateRoomPayload, Id, MemberPermission, RoomAdminOp,
|
ChatPayload, CreateGroup, CreatePeerChat, CreateRoomPayload, Id, MemberPermission, RoomAdminOp,
|
||||||
RoomAdminPayload, RoomAttrs, RoomMetadata, ServerPermission, SignedChatMsg, Signee, UserKey,
|
RoomAdminPayload, RoomAttrs, RoomMetadata, ServerPermission, Signed, SignedChatMsg, Signee,
|
||||||
WithMsgId, WithSig,
|
UserKey, WithMsgId,
|
||||||
};
|
};
|
||||||
use config::ServerConfig;
|
use config::ServerConfig;
|
||||||
use ed25519_dalek::SIGNATURE_LENGTH;
|
use ed25519_dalek::SIGNATURE_LENGTH;
|
||||||
|
@ -59,7 +59,7 @@ impl AppState {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn verify_signed_data<T: Serialize>(&self, data: &WithSig<T>) -> Result<(), ApiError> {
|
fn verify_signed_data<T: Serialize>(&self, data: &Signed<T>) -> Result<(), ApiError> {
|
||||||
let Ok(()) = data.verify() else {
|
let Ok(()) = data.verify() else {
|
||||||
return Err(error_response!(
|
return Err(error_response!(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
|
|
|
@ -6,7 +6,7 @@ use axum::extract::{FromRef, FromRequest, FromRequestParts, Request};
|
||||||
use axum::http::{header, request, StatusCode};
|
use axum::http::{header, request, StatusCode};
|
||||||
use axum::response::{IntoResponse, Response};
|
use axum::response::{IntoResponse, Response};
|
||||||
use axum::{async_trait, Json};
|
use axum::{async_trait, Json};
|
||||||
use blah_types::{AuthPayload, UserKey, WithSig};
|
use blah_types::{AuthPayload, Signed, UserKey};
|
||||||
use serde::de::DeserializeOwned;
|
use serde::de::DeserializeOwned;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -95,7 +95,7 @@ impl From<rusqlite::Error> for ApiError {
|
||||||
|
|
||||||
/// Extractor for verified JSON payload.
|
/// Extractor for verified JSON payload.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct SignedJson<T>(pub WithSig<T>);
|
pub struct SignedJson<T>(pub Signed<T>);
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<S, T> FromRequest<S> for SignedJson<T>
|
impl<S, T> FromRequest<S> for SignedJson<T>
|
||||||
|
@ -107,7 +107,7 @@ where
|
||||||
type Rejection = ApiError;
|
type Rejection = ApiError;
|
||||||
|
|
||||||
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request(req: Request, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
let Json(data) = <Json<WithSig<T>> as FromRequest<S>>::from_request(req, state).await?;
|
let Json(data) = <Json<Signed<T>> as FromRequest<S>>::from_request(req, state).await?;
|
||||||
let st = <Arc<AppState>>::from_ref(state);
|
let st = <Arc<AppState>>::from_ref(state);
|
||||||
st.verify_signed_data(&data)?;
|
st.verify_signed_data(&data)?;
|
||||||
Ok(Self(data))
|
Ok(Self(data))
|
||||||
|
@ -178,7 +178,7 @@ where
|
||||||
|
|
||||||
let st = <Arc<AppState>>::from_ref(state);
|
let st = <Arc<AppState>>::from_ref(state);
|
||||||
let data =
|
let data =
|
||||||
serde_json::from_slice::<WithSig<AuthPayload>>(auth.as_bytes()).map_err(|err| {
|
serde_json::from_slice::<Signed<AuthPayload>>(auth.as_bytes()).map_err(|err| {
|
||||||
AuthRejection::Invalid(error_response!(
|
AuthRejection::Invalid(error_response!(
|
||||||
StatusCode::BAD_REQUEST,
|
StatusCode::BAD_REQUEST,
|
||||||
"deserialization",
|
"deserialization",
|
||||||
|
|
|
@ -9,7 +9,7 @@ use anyhow::Result;
|
||||||
use blah_types::{
|
use blah_types::{
|
||||||
get_timestamp, AuthPayload, ChatPayload, CreateGroup, CreatePeerChat, CreateRoomPayload, Id,
|
get_timestamp, AuthPayload, ChatPayload, CreateGroup, CreatePeerChat, CreateRoomPayload, Id,
|
||||||
MemberPermission, RichText, RoomAdminOp, RoomAdminPayload, RoomAttrs, RoomMember,
|
MemberPermission, RichText, RoomAdminOp, RoomAdminPayload, RoomAttrs, RoomMember,
|
||||||
RoomMemberList, RoomMetadata, ServerPermission, SignedChatMsg, UserKey, WithMsgId, WithSig,
|
RoomMemberList, RoomMetadata, ServerPermission, Signed, SignedChatMsg, UserKey, WithMsgId,
|
||||||
};
|
};
|
||||||
use blahd::{ApiError, AppState, Database, RoomList, RoomMsgs};
|
use blahd::{ApiError, AppState, Database, RoomList, RoomMsgs};
|
||||||
use ed25519_dalek::SigningKey;
|
use ed25519_dalek::SigningKey;
|
||||||
|
@ -241,8 +241,8 @@ async fn smoke(server: Server) {
|
||||||
assert_eq!(got, exp);
|
assert_eq!(got, exp);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn sign<T: Serialize>(key: &SigningKey, rng: &mut dyn RngCore, payload: T) -> WithSig<T> {
|
fn sign<T: Serialize>(key: &SigningKey, rng: &mut dyn RngCore, payload: T) -> Signed<T> {
|
||||||
WithSig::sign(key, get_timestamp(), rng, payload).unwrap()
|
Signed::sign(key, get_timestamp(), rng, payload).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auth(key: &SigningKey, rng: &mut impl RngCore) -> String {
|
fn auth(key: &SigningKey, rng: &mut impl RngCore) -> String {
|
||||||
|
|
|
@ -13,7 +13,7 @@ paths:
|
||||||
This endpoint is for server-side-event dispatching.
|
This endpoint is for server-side-event dispatching.
|
||||||
|
|
||||||
Once connected, client must send a JSON text message of type
|
Once connected, client must send a JSON text message of type
|
||||||
`WithSig-Auth` for authentication.
|
`Signed-Auth` for authentication.
|
||||||
If server does not close it immediately, it means success.
|
If server does not close it immediately, it means success.
|
||||||
|
|
||||||
Since OAPI does not support WebSocket interface, we use request and
|
Since OAPI does not support WebSocket interface, we use request and
|
||||||
|
@ -88,7 +88,7 @@ paths:
|
||||||
in: header
|
in: header
|
||||||
description: Optional proof of membership for private rooms.
|
description: Optional proof of membership for private rooms.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/WithSig-Auth'
|
$ref: '#/components/schemas/Signed-Auth'
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
|
@ -119,7 +119,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/WithSig-CreateRoom'
|
$ref: '#/components/schemas/Signed-CreateRoom'
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
|
@ -159,7 +159,7 @@ paths:
|
||||||
in: header
|
in: header
|
||||||
description: Optional proof of membership for private rooms.
|
description: Optional proof of membership for private rooms.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/WithSig-Auth'
|
$ref: '#/components/schemas/Signed-Auth'
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
|
@ -185,7 +185,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/WithSig-RoomAdmin'
|
$ref: '#/components/schemas/Signed-RoomAdmin'
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
204:
|
204:
|
||||||
|
@ -244,7 +244,7 @@ paths:
|
||||||
in: header
|
in: header
|
||||||
description: Optional proof of membership for private rooms.
|
description: Optional proof of membership for private rooms.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/WithSig-Auth'
|
$ref: '#/components/schemas/Signed-Auth'
|
||||||
|
|
||||||
- name: top
|
- name: top
|
||||||
in: query
|
in: query
|
||||||
|
@ -284,7 +284,7 @@ paths:
|
||||||
content:
|
content:
|
||||||
application/json:
|
application/json:
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/WithSig-Chat'
|
$ref: '#/components/schemas/Signed-Chat'
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
200:
|
200:
|
||||||
|
@ -325,7 +325,7 @@ paths:
|
||||||
required: true
|
required: true
|
||||||
description: Proof of membership for private rooms.
|
description: Proof of membership for private rooms.
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/components/schemas/WithSig-Auth'
|
$ref: '#/components/schemas/Signed-Auth'
|
||||||
|
|
||||||
responses:
|
responses:
|
||||||
204:
|
204:
|
||||||
|
@ -345,14 +345,14 @@ components:
|
||||||
schemas:
|
schemas:
|
||||||
WSClientToServer:
|
WSClientToServer:
|
||||||
anyOf:
|
anyOf:
|
||||||
- $ref: '#/components/schemas/WithSig-Auth'
|
- $ref: '#/components/schemas/Signed-Auth'
|
||||||
|
|
||||||
WSServerToClient:
|
WSServerToClient:
|
||||||
anyOf:
|
anyOf:
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
chat:
|
chat:
|
||||||
$ref: '#/components/schemas/WithSig-Chat'
|
$ref: '#/components/schemas/Signed-Chat'
|
||||||
|
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
|
@ -401,7 +401,7 @@ components:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
last_msg:
|
last_msg:
|
||||||
$ref: '#/components/schemas/WithMsgId-WithSig-Chat'
|
$ref: '#/components/schemas/WithMsgId-Signed-Chat'
|
||||||
last_seen_cid:
|
last_seen_cid:
|
||||||
description: The `cid` of the last chat being marked as seen.
|
description: The `cid` of the last chat being marked as seen.
|
||||||
type: string
|
type: string
|
||||||
|
@ -440,7 +440,7 @@ components:
|
||||||
description: Room messages in reversed server-received time order.
|
description: Room messages in reversed server-received time order.
|
||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/components/schemas/WithMsgId-WithSig-Chat'
|
$ref: '#/components/schemas/WithMsgId-Signed-Chat'
|
||||||
skip_token:
|
skip_token:
|
||||||
description: The token for fetching the next page.
|
description: The token for fetching the next page.
|
||||||
type: string
|
type: string
|
||||||
|
@ -481,7 +481,7 @@ components:
|
||||||
description: Link target.
|
description: Link target.
|
||||||
|
|
||||||
|
|
||||||
WithSig-Auth:
|
Signed-Auth:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
sig:
|
sig:
|
||||||
|
@ -499,7 +499,7 @@ components:
|
||||||
type: string
|
type: string
|
||||||
const: 'auth'
|
const: 'auth'
|
||||||
|
|
||||||
WithSig-RoomAdmin:
|
Signed-RoomAdmin:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
sig:
|
sig:
|
||||||
|
@ -551,7 +551,7 @@ components:
|
||||||
timestamp: 1724966284
|
timestamp: 1724966284
|
||||||
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
|
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
|
||||||
|
|
||||||
WithSig-Chat:
|
Signed-Chat:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
sig:
|
sig:
|
||||||
|
@ -583,16 +583,16 @@ components:
|
||||||
timestamp: 1724966284
|
timestamp: 1724966284
|
||||||
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
|
user: 83ce46ced47ec0391c64846cbb6c507250ead4985b6a044d68751edc46015dd7
|
||||||
|
|
||||||
WithMsgId-WithSig-Chat:
|
WithMsgId-Signed-Chat:
|
||||||
allOf:
|
allOf:
|
||||||
- $ref: '#/components/schemas/WithSig-Chat'
|
- $ref: '#/components/schemas/Signed-Chat'
|
||||||
- type: object
|
- type: object
|
||||||
properties:
|
properties:
|
||||||
cid:
|
cid:
|
||||||
type: string
|
type: string
|
||||||
description: An opaque server-specific identifier.
|
description: An opaque server-specific identifier.
|
||||||
|
|
||||||
WithSig-CreateRoom:
|
Signed-CreateRoom:
|
||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
sig:
|
sig:
|
||||||
|
|
Loading…
Add table
Reference in a new issue