use axum::http::StatusCode;
use serde::Deserialize;
use super::common::{ApiResult, bad_payload, err, internal_error_str};
use super::payload::PublishPayload;
use crate::cloud_server::auth::{generate_token, sha256_hex};
use crate::cloud_server::helpers::internal_error;
pub(in crate::cloud_server) const PUBLISHER_ID_HEX_LEN: usize = 32;
pub(in crate::cloud_server) const EDIT_TOKEN_CHALLENGE_TTL_MINUTES: i32 = 5;
#[derive(Deserialize)]
pub(in crate::cloud_server) struct SignedEnvelope {
pub(in crate::cloud_server) payload_json: String,
pub(in crate::cloud_server) public_key: Option<String>,
pub(in crate::cloud_server) signature: Option<String>,
}
pub(in crate::cloud_server) fn publisher_id_from_public_key_hex(
public_key: &str,
) -> ApiResult<String> {
sha256_hex(public_key)
.get(..PUBLISHER_ID_HEX_LEN)
.ok_or_else(internal_error_str)
.map(str::to_string)
}
pub(in crate::cloud_server) fn verify_signed_envelope(
env: &SignedEnvelope,
) -> ApiResult<(PublishPayload, String)> {
use crate::core::agent_identity::{hex_decode, verify_signature};
let (Some(pk_hex), Some(sig_hex)) = (&env.public_key, &env.signature) else {
return Err(bad_payload());
};
let pk_bytes = hex_decode(pk_hex).map_err(|_| bad_payload())?;
let sig_bytes = hex_decode(sig_hex).map_err(|_| bad_payload())?;
if !verify_signature(&pk_bytes, env.payload_json.as_bytes(), &sig_bytes) {
return Err(err(StatusCode::UNAUTHORIZED, "invalid_signature"));
}
let payload: PublishPayload =
serde_json::from_str(&env.payload_json).map_err(|_| bad_payload())?;
let publisher_id = publisher_id_from_public_key_hex(pk_hex)?;
Ok((payload, publisher_id))
}
pub(in crate::cloud_server) async fn mint_edit_token_challenge(
client: &tokio_postgres::Client,
card_id: &str,
) -> ApiResult<String> {
let nonce = generate_token();
client
.execute(
"INSERT INTO wrapped_edit_token_challenges (nonce_hash, card_id, expires_at) \
VALUES ($1, $2, now() + make_interval(mins => $3)) \
ON CONFLICT (card_id) DO UPDATE \
SET nonce_hash = EXCLUDED.nonce_hash, \
created_at = now(), \
expires_at = EXCLUDED.expires_at",
&[
&sha256_hex(&nonce),
&card_id,
&EDIT_TOKEN_CHALLENGE_TTL_MINUTES,
],
)
.await
.map_err(internal_error)?;
Ok(nonce)
}