Fix genesis commit stuff, update deps

This commit is contained in:
lewis
2025-12-23 22:37:44 +02:00
parent cb98398494
commit 091d6d96a0
6 changed files with 333 additions and 506 deletions
+6 -27
View File
@@ -1,4 +1,5 @@
use super::did::verify_did_web;
use crate::api::repo::record::utils::create_signed_commit;
use crate::auth::{ServiceTokenVerifier, extract_bearer_token_from_header, is_service_token};
use crate::plc::{PlcClient, create_genesis_operation, signing_key_to_did_key};
use crate::state::{AppState, RateLimitKind};
@@ -10,8 +11,8 @@ use axum::{
response::{IntoResponse, Response},
};
use bcrypt::{DEFAULT_COST, hash};
use jacquard::types::{did::Did, integer::LimitedU32, string::Tid};
use jacquard_repo::{commit::Commit, mst::Mst, storage::BlockStore};
use jacquard::types::{integer::LimitedU32, string::Tid};
use jacquard_repo::{mst::Mst, storage::BlockStore};
use k256::{SecretKey, ecdsa::SigningKey};
use rand::rngs::OsRng;
use serde::{Deserialize, Serialize};
@@ -864,33 +865,11 @@ pub async fn create_account(
.into_response();
}
};
let did_obj = match Did::new(&did) {
Ok(d) => d,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError", "message": "Invalid DID"})),
)
.into_response();
}
};
let rev = Tid::now(LimitedU32::MIN);
let unsigned_commit = Commit::new_unsigned(did_obj, mst_root, rev, None);
let signed_commit = match unsigned_commit.sign(&signing_key) {
Ok(c) => c,
let (commit_bytes, _sig) = match create_signed_commit(&did, mst_root, &rev.to_string(), None, &signing_key) {
Ok(result) => result,
Err(e) => {
error!("Error signing genesis commit: {:?}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
)
.into_response();
}
};
let commit_bytes = match signed_commit.to_cbor() {
Ok(b) => b,
Err(e) => {
error!("Error serializing genesis commit: {:?}", e);
error!("Error creating genesis commit: {:?}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
+11 -11
View File
@@ -3,23 +3,23 @@ use bytes::Bytes;
use cid::Cid;
use jacquard::types::{integer::LimitedU32, string::Tid};
use jacquard_repo::storage::BlockStore;
use k256::ecdsa::{Signature, SigningKey, signature::Signer};
use k256::ecdsa::{signature::Signer, Signature, SigningKey};
use serde::Serialize;
use serde_json::json;
use uuid::Uuid;
/*
* Why am I making custom commit objects instead of jacquard's Commit::sign(), you ask?
* Why custom commit signing instead of jacquard's Commit::sign()?
*
* At time of writing, jacquard has a bug in how it creates unsigned bytes for signing.
* Jacquard sets sig to empty bytes and serializes (6-field CBOR map)
* Indigo/ATProto creates a struct *without* the sig field (5-field CBOR map)
* Jacquard previously had a bug in how it created unsigned bytes for signing:
* it set sig to empty bytes and serialized (6-field CBOR map), while the
* ATProto spec creates a struct *without* the sig field (5-field CBOR map).
* These produce different CBOR bytes, so signatures didn't verify with relays.
*
* These produce different CBOR bytes, so signatures created with jacquard
* don't verify with the relay's algorithm. The relay silently rejects commits
* with invalid signatures.
*
* If you have it downloaded, see: reference-relay-indigo/atproto/repo/commit.go UnsignedBytes()
* The bug has been fixed in jacquard, but the fix is untested here.
* TODO: Switch back to jacquard's Commit::sign() and verify it works.
*/
#[derive(Serialize)]
struct UnsignedCommit<'a> {
data: Cid,
@@ -29,7 +29,7 @@ struct UnsignedCommit<'a> {
version: i64,
}
fn create_signed_commit(
pub fn create_signed_commit(
did: &str,
data: Cid,
rev: &str,
+36 -29
View File
@@ -6,8 +6,8 @@ use axum::{
};
use bcrypt::{DEFAULT_COST, hash};
use chrono::{Duration, Utc};
use jacquard::types::{did::Did, integer::LimitedU32, string::Tid};
use jacquard_repo::{commit::Commit, mst::Mst, storage::BlockStore};
use jacquard::types::{integer::LimitedU32, string::Tid};
use jacquard_repo::{mst::Mst, storage::BlockStore};
use rand::Rng;
use serde::{Deserialize, Serialize};
use serde_json::json;
@@ -15,6 +15,7 @@ use std::sync::Arc;
use tracing::{error, info, warn};
use uuid::Uuid;
use crate::api::repo::record::utils::create_signed_commit;
use crate::state::{AppState, RateLimitKind};
use crate::validation::validate_password;
@@ -391,13 +392,20 @@ pub async fn create_passkey_account(
}
};
let is_first_user = sqlx::query_scalar!("SELECT COUNT(*) as count FROM users")
.fetch_one(&mut *tx)
.await
.map(|c| c.unwrap_or(0) == 0)
.unwrap_or(false);
let user_insert: Result<(Uuid,), _> = sqlx::query_as(
r#"INSERT INTO users (
handle, email, did, password_hash, password_required,
preferred_comms_channel,
discord_id, telegram_username, signal_number,
recovery_token, recovery_token_expires_at
) VALUES ($1, $2, $3, NULL, FALSE, $4::comms_channel, $5, $6, $7, $8, $9) RETURNING id"#,
recovery_token, recovery_token_expires_at,
is_admin
) VALUES ($1, $2, $3, NULL, FALSE, $4::comms_channel, $5, $6, $7, $8, $9, $10) RETURNING id"#,
)
.bind(&handle)
.bind(&email)
@@ -426,6 +434,7 @@ pub async fn create_passkey_account(
)
.bind(&setup_token_hash)
.bind(setup_expires_at)
.bind(is_first_user)
.fetch_one(&mut *tx)
.await;
@@ -518,33 +527,11 @@ pub async fn create_passkey_account(
.into_response();
}
};
let did_obj = match Did::new(&did) {
Ok(d) => d,
Err(_) => {
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError", "message": "Invalid DID"})),
)
.into_response();
}
};
let rev = Tid::now(LimitedU32::MIN);
let unsigned_commit = Commit::new_unsigned(did_obj, mst_root, rev, None);
let signed_commit = match unsigned_commit.sign(&secret_key) {
Ok(c) => c,
let (commit_bytes, _sig) = match create_signed_commit(&did, mst_root, &rev.to_string(), None, &secret_key) {
Ok(result) => result,
Err(e) => {
error!("Error signing genesis commit: {:?}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
)
.into_response();
}
};
let commit_bytes = match signed_commit.to_cbor() {
Ok(b) => b,
Err(e) => {
error!("Error serializing genesis commit: {:?}", e);
error!("Error creating genesis commit: {:?}", e);
return (
StatusCode::INTERNAL_SERVER_ERROR,
Json(json!({"error": "InternalError"})),
@@ -630,6 +617,26 @@ pub async fn create_passkey_account(
{
warn!("Failed to sequence identity event for {}: {}", did, e);
}
if let Err(e) =
crate::api::repo::record::sequence_account_event(&state, &did, true, None).await
{
warn!("Failed to sequence account event for {}: {}", did, e);
}
let profile_record = serde_json::json!({
"$type": "app.bsky.actor.profile",
"displayName": handle
});
if let Err(e) = crate::api::repo::record::create_record_internal(
&state,
&did,
"app.bsky.actor.profile",
"self",
&profile_record,
)
.await
{
warn!("Failed to create default profile for {}: {}", did, e);
}
if let Err(e) = crate::comms::enqueue_signup_verification(
&state.db,
+1
View File
@@ -90,6 +90,7 @@ impl ClientMetadataCache {
if let Ok(url) = reqwest::Url::parse(client_id) {
url.scheme() == "http"
&& matches!(url.host_str(), Some("localhost") | Some("127.0.0.1"))
&& url.query().is_some()
} else {
false
}