Skip to content

Commit 0118f23

Browse files
authored
only accept secp256k1 ecies encrypted uploads (#2)
1 parent 3c8ad3f commit 0118f23

File tree

3 files changed

+28
-12
lines changed

3 files changed

+28
-12
lines changed

Cargo.lock

Lines changed: 8 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ nostr-relay-builder = { git = "https://github.com/BitcreditProtocol/nostr.git",
1313
nostr-sqldb = { git = "https://github.com/BitcreditProtocol/nostr.git", branch = "sqldb", features = [
1414
"postgres",
1515
] }
16-
tokio = "1.44.1"
16+
tokio = "1.45.1"
1717
axum = { version = "0.8.3", features = ["ws", "tokio"] }
1818
axum-raw-websocket = { git = "https://github.com/tompro/axum-raw-websocket.git" }
19-
anyhow = "1.0.97"
19+
anyhow = "1"
2020
tracing = "0.1.41"
2121
tracing-subscriber = "0.3.19"
22-
tower-http = { version = "0.6.2", features = ["cors"] }
23-
clap = { version = "4.5.29", features = ["derive", "env"] }
22+
tower-http = { version = "0.6.6", features = ["cors"] }
23+
clap = { version = "4.5.39", features = ["derive", "env"] }
2424
chrono = "0.4.41"
2525
serde_json = "1"
2626
serde = { version = "1", features = ["derive"] }

src/blossom/mod.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ use tracing::{error, info};
2222
use crate::AppState;
2323

2424
const MAX_FILE_SIZE_BYTES: usize = 1_000_000; // ~1 MB
25+
const ENCRYPTION_PUB_KEY_BYTE_LEN: usize = 65; // we use uncompressed keys
2526

2627
/// For now, the only parts of the API we implement are
2728
/// GET /<sha256> - get a file
@@ -63,6 +64,7 @@ pub async fn handle_upload(State(state): State<AppState>, body: Bytes) -> impl I
6364
let size = body.len();
6465

6566
info!("Upload File called for {} bytes", size);
67+
// check size
6668
if size > MAX_FILE_SIZE_BYTES {
6769
return (
6870
StatusCode::PAYLOAD_TOO_LARGE,
@@ -74,7 +76,19 @@ pub async fn handle_upload(State(state): State<AppState>, body: Bytes) -> impl I
7476
if size == 0 {
7577
return (StatusCode::BAD_REQUEST, "Empty body").into_response();
7678
}
79+
// validate it's an ECIES/secp256k1 encrypted blob by checking if it starts with an ephemeral secp256k1 pub key
80+
// this is not a 100% guarantee (which is impossible), but rather a pretty reliable heuristic
81+
if size < ENCRYPTION_PUB_KEY_BYTE_LEN {
82+
error!("Non-encrypted Upload rejected - not big enough");
83+
return (StatusCode::BAD_REQUEST, "Invalid body").into_response();
84+
}
85+
let pubkey_bytes = &body[0..ENCRYPTION_PUB_KEY_BYTE_LEN];
86+
if let Err(e) = nostr::secp256k1::PublicKey::from_slice(pubkey_bytes) {
87+
error!("Non-encrypted Upload rejected: {e}");
88+
return (StatusCode::BAD_REQUEST, "Invalid body").into_response();
89+
}
7790

91+
// create hash
7892
let mut hash_engine = sha256::HashEngine::default();
7993
if let Err(e) = hash_engine.write_all(&body) {
8094
error!("Error while hashing {size} bytes: {e}");
@@ -88,11 +102,13 @@ pub async fn handle_upload(State(state): State<AppState>, body: Bytes) -> impl I
88102
size: size as i32,
89103
};
90104

105+
// store
91106
if let Err(e) = state.file_store.insert(file).await {
92107
error!("Error while storing {size} bytes with hash {hash}: {e}");
93108
return (StatusCode::INTERNAL_SERVER_ERROR, "INTERNAL_SERVER_ERROR").into_response();
94109
}
95110

111+
// return blob descriptor
96112
let blob_desc = BlobDescriptor::new(state.cfg.host_url, hash, size).unwrap();
97113
(StatusCode::OK, Json(blob_desc)).into_response()
98114
}

0 commit comments

Comments
 (0)