Skip to content

Commit

Permalink
refactor: moving UpdateEntryJson to webserver, removing id from it
Browse files Browse the repository at this point in the history
  • Loading branch information
distractedm1nd committed Jul 18, 2024
1 parent 55f1d24 commit a6a3e99
Show file tree
Hide file tree
Showing 6 changed files with 114 additions and 109 deletions.
3 changes: 1 addition & 2 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ The update operation causes either the hashchain for an existing entry to be upd
```bash
curl -X POST http://localhost:8080/update-entry \
-H "Content-Type: application/json" \
-d '{ "id": "YOUR_ID", \
"public_key": "YOUR_PUBLIC_KEY", \
-d '{ "public_key": "YOUR_PUBLIC_KEY", \
"signed_message": "YOUR_SIGNED_MESSAGE"}'
```

Expand Down
7 changes: 3 additions & 4 deletions src/node_types/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ pub trait NodeType {

#[cfg(test)]
mod tests {
use crate::{storage::UpdateEntryJson, utils::verify_signature};
use crate::{utils::verify_signature, webserver::UpdateEntryJson};
use base64::{engine::general_purpose, Engine as _};

fn setup_signature(valid_signature: bool) -> UpdateEntryJson {
Expand All @@ -25,8 +25,7 @@ mod tests {
let id_public_key = "CosRXOoSLG7a8sCGx78KhtfLEuiyNY7L4ksFt78mp2M=".to_string();

UpdateEntryJson {
id: id_public_key.clone(),
signed_message,
signed_incoming_entry: signed_message,
public_key: id_public_key,
}
}
Expand Down Expand Up @@ -61,7 +60,7 @@ mod tests {
let short_message = general_purpose::STANDARD.encode("this is a short message");

let signature_with_key = UpdateEntryJson {
signed_message: short_message,
signed_incoming_entry: short_message,
..signature_with_key
};

Expand Down
46 changes: 22 additions & 24 deletions src/node_types/sequencer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,9 @@ use crate::{
da::{DataAvailabilityLayer, EpochJson},
error::{DeimosError, GeneralError},
node_types::NodeType,
storage::{ChainEntry, Database, IncomingEntry, Operation, UpdateEntryJson},
storage::{ChainEntry, Database, IncomingEntry, Operation},
utils::verify_signature,
webserver::UpdateEntryJson,
webserver::WebServer,
zk_snark::BatchMerkleProofCircuit,
};
Expand Down Expand Up @@ -359,25 +360,20 @@ impl Sequencer {
///
/// # Arguments
///
/// * `operation` - An `Operation` enum variant representing the type of operation to be performed (Add or Revoke).
/// * `incoming_entry` - A reference to an `IncomingEntry` struct containing the key and the entry data to be updated.
/// * `signature` - A `Signature` struct representing the signature.
pub fn update_entry(&self, signature: &UpdateEntryJson) -> DeimosResult<()> {
debug!(
"updating entry for uid {} with msg {}",
signature.id, signature.signed_message
);
let signed_content = match verify_signature(signature, Some(signature.public_key.clone())) {
Ok(content) => content,
Err(_) => {
// TODO(@distractedm1nd): Add to error instead of logging
error!(
"updating entry for uid {}: invalid signature with pubkey {} on msg {}",
signature.id, signature.public_key, signature.signed_message
);
return Err(GeneralError::InvalidSignature.into());
}
};
/// * `signed_entry` - A `UpdateEntryJson` object.
pub fn update_entry(&self, signed_entry: &UpdateEntryJson) -> DeimosResult<()> {
let signed_content =
match verify_signature(signed_entry, Some(signed_entry.public_key.clone())) {
Ok(content) => content,
Err(_) => {
// TODO(@distractedm1nd): Add to error instead of logging
error!(
"updating entry: invalid signature with pubkey {} on msg {}",
signed_entry.public_key, signed_entry.signed_incoming_entry
);
return Err(GeneralError::InvalidSignature.into());
}
};

let message_obj: IncomingEntry = match serde_json::from_str(&signed_content) {
Ok(obj) => obj,
Expand All @@ -386,14 +382,16 @@ impl Sequencer {
}
};

let id = message_obj.id.clone();

// check with given key if the signature is valid
let incoming_entry = IncomingEntry {
id: signature.id.clone(),
id: id.clone(),
operation: message_obj.operation,
value: message_obj.value,
};
// add a new key to an existing id ( type for the value retrieved from the database explicitly set to string)
match self.db.get_hashchain(&signature.id) {
match self.db.get_hashchain(&id) {
Ok(value) => {
// hashchain already exists
let mut current_chain = value.clone();
Expand All @@ -402,7 +400,7 @@ impl Sequencer {
None => {
return Err(DatabaseError::NotFoundError(format!(
"last value in hashchain for incoming entry with id {}",
signature.id.clone()
id.clone()
))
.into());
}
Expand Down Expand Up @@ -469,7 +467,7 @@ impl Sequencer {
None => {
return Err(DatabaseError::ReadError(format!(
"last value in hashchain for incoming entry with id {}",
signature.id.clone()
id.clone()
))
.into());
}
Expand Down
66 changes: 1 addition & 65 deletions src/storage.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use base64::engine::{general_purpose, Engine as _};
use ed25519::Signature;
use indexed_merkle_tree::{node::Node, sha256_mod, tree::Proof};
use mockall::{predicate::*, *};
use redis::{Client, Commands, Connection};
Expand All @@ -16,7 +14,7 @@ use std::{
use crate::{
cfg::RedisConfig,
error::{DatabaseError, DeimosError, DeimosResult, GeneralError},
utils::{parse_json_to_proof, Signable},
utils::parse_json_to_proof,
};

#[derive(Clone, Serialize, Deserialize, Debug, PartialEq)]
Expand Down Expand Up @@ -61,68 +59,6 @@ pub struct IncomingEntry {
pub value: String,
}

#[derive(Deserialize, Debug)]
pub struct UpdateEntryJson {
pub id: String,
pub signed_message: String,
pub public_key: String,
}

fn decode_signed_message(signed_message: &String) -> DeimosResult<Vec<u8>> {
let signed_message_bytes = general_purpose::STANDARD
.decode(signed_message)
.map_err(|e| {
DeimosError::General(GeneralError::DecodingError(format!(
"signed message: {}",
e
)))
})?;

// check if the signed message is (at least) 64 bytes long
if signed_message_bytes.len() < 64 {
Err(GeneralError::ParsingError(format!(
"signed message is too short: {} < 64",
signed_message_bytes.len(),
))
.into())
} else {
Ok(signed_message_bytes)
}
}

impl Signable for UpdateEntryJson {
fn get_signature(&self) -> DeimosResult<Signature> {
let signed_message_bytes = decode_signed_message(&self.signed_message)?;

// extract the first 64 bytes from the signed message which are the signature
let signature_bytes: &[u8; 64] = match signed_message_bytes.get(..64) {
Some(array_section) => match array_section.try_into() {
Ok(array) => array,
Err(e) => Err(DeimosError::General(GeneralError::DecodingError(format!(
"signed message to array: {}",
e
))))?,
},
None => Err(DeimosError::General(GeneralError::DecodingError(format!(
"extracting signature from signed message: {}",
&self.signed_message
))))?,
};

Ok(Signature::from_bytes(signature_bytes))
}

fn get_content_to_sign(&self) -> DeimosResult<String> {
let signed_message_bytes = decode_signed_message(&self.signed_message)?;
let message_bytes = &signed_message_bytes[64..];
Ok(String::from_utf8_lossy(message_bytes).to_string())
}

fn get_public_key(&self) -> DeimosResult<String> {
Ok(self.public_key.clone())
}
}

pub struct RedisConnections {
pub main_dict: Mutex<Connection>, // clear text key with hashchain
pub derived_dict: Mutex<Connection>, // hashed key with last hashchain entry hash
Expand Down
20 changes: 20 additions & 0 deletions src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,26 @@ pub trait Signable {
fn get_public_key(&self) -> DeimosResult<String>;
}

pub fn decode_signed_message(signed_message: &String) -> DeimosResult<Vec<u8>> {
let signed_message_bytes = engine.decode(signed_message).map_err(|e| {
DeimosError::General(GeneralError::DecodingError(format!(
"signed message: {}",
e
)))
})?;

// check if the signed message is (at least) 64 bytes long
if signed_message_bytes.len() < 64 {
Err(GeneralError::ParsingError(format!(
"signed message is too short: {} < 64",
signed_message_bytes.len(),
))
.into())
} else {
Ok(signed_message_bytes)
}
}

// verifies the signature of a given signable item and returns the content of the item if the signature is valid
pub fn verify_signature<T: Signable>(
item: &T,
Expand Down
81 changes: 67 additions & 14 deletions src/webserver.rs
Original file line number Diff line number Diff line change
@@ -1,24 +1,23 @@
use crate::{
cfg::WebServerConfig,
error::{DeimosError, DeimosResult, GeneralError},
node_types::sequencer::Sequencer,
storage::{ChainEntry, IncomingEntry},
utils::{decode_signed_message, is_not_revoked, Signable},
};
use actix_cors::Cors;
use actix_web::{
dev::Server,
get, post,
web::{self, Data},
App as ActixApp, HttpResponse, HttpServer, Responder,
};
use ed25519::Signature;
use indexed_merkle_tree::{sha256_mod, tree::Proof};
use serde::{Deserialize, Serialize};
use serde_json::{self, json, Value};

use std::sync::Arc;

use crate::{
cfg::WebServerConfig,
error::DeimosResult,
node_types::sequencer::Sequencer,
storage::{ChainEntry, UpdateEntryJson},
utils::is_not_revoked,
};

pub struct WebServer {
pub cfg: WebServerConfig,
}
Expand All @@ -31,6 +30,45 @@ pub struct EpochData {
proofs: Vec<Proof>,
}

#[derive(Deserialize, Debug)]
pub struct UpdateEntryJson {
pub signed_incoming_entry: String,
pub public_key: String,
}

impl Signable for UpdateEntryJson {
fn get_signature(&self) -> DeimosResult<Signature> {
let signed_message_bytes = decode_signed_message(&self.signed_incoming_entry)?;

// extract the first 64 bytes from the signed message which are the signature
let signature_bytes: &[u8; 64] = match signed_message_bytes.get(..64) {
Some(array_section) => match array_section.try_into() {
Ok(array) => array,
Err(e) => Err(DeimosError::General(GeneralError::DecodingError(format!(
"signed message to array: {}",
e
))))?,
},
None => Err(DeimosError::General(GeneralError::DecodingError(format!(
"extracting signature from signed message: {}",
&self.signed_incoming_entry
))))?,
};

Ok(Signature::from_bytes(signature_bytes))
}

fn get_content_to_sign(&self) -> DeimosResult<String> {
let signed_message_bytes = decode_signed_message(&self.signed_incoming_entry)?;
let message_bytes = &signed_message_bytes[64..];
Ok(String::from_utf8_lossy(message_bytes).to_string())
}

fn get_public_key(&self) -> DeimosResult<String> {
Ok(self.public_key.clone())
}
}

impl WebServer {
pub fn new(cfg: WebServerConfig) -> Self {
WebServer { cfg }
Expand Down Expand Up @@ -69,9 +107,7 @@ impl WebServer {
///
/// * `req_body` - A JSON string containing the information needed to update or insert an entry in the dictionary.
/// The JSON string should have the following fields:
/// - `operation`: An `Operation` enum indicating whether the operation is an add or revoke operation.
/// - `incoming_entry`: An `IncomingEntry` object containing the id and the public key.
/// - `private_key`: A string representing the private key used to sign the incoming entry. (TODO! bessere Lösung finden)
/// - `signed_message`: An `UpdateEntryJson` object containing the id, operation, and value, signed by the public key.
///
/// # Returns
///
Expand All @@ -91,6 +127,23 @@ async fn update_entry(
}
};

let incoming_entry_json = match signature_with_key.get_content_to_sign() {
Ok(entry) => entry,
Err(e) => {
return HttpResponse::BadRequest().json(format!(
"Error retrieving content from UpdateEntryJson: {}",
e
))
}
};

let incoming_entry: IncomingEntry = match serde_json::from_str(&incoming_entry_json) {
Ok(entry) => entry,
Err(e) => {
return HttpResponse::BadRequest().json(format!("Error decoding signed content: {}", e))
}
};

let epoch = match session.db.get_epoch() {
Ok(e) => e,
Err(e) => {
Expand All @@ -113,7 +166,7 @@ async fn update_entry(
}
};

let result: DeimosResult<Vec<ChainEntry>> = session.db.get_hashchain(&signature_with_key.id);
let result: DeimosResult<Vec<ChainEntry>> = session.db.get_hashchain(&incoming_entry.id);
let update_proof = result.is_ok();

match session.update_entry(&signature_with_key) {
Expand All @@ -125,7 +178,7 @@ async fn update_entry(
.json(format!("Error creating new tree: {}", e))
}
};
let hashed_id = sha256_mod(&signature_with_key.id);
let hashed_id = sha256_mod(&incoming_entry.id);
let mut node = match new_tree.find_leaf_by_label(&hashed_id) {
Some(n) => n,
None => return HttpResponse::InternalServerError().json("Error finding leaf"),
Expand Down

0 comments on commit a6a3e99

Please sign in to comment.