From 781d3a291ca2d100f57b94d8d8efa498a7fabc87 Mon Sep 17 00:00:00 2001 From: Martin Stefcek <35243812+Cifko@users.noreply.github.com> Date: Wed, 27 Nov 2024 18:18:17 +0100 Subject: [PATCH] chore: move verify signature to atoma-utils (#254) * chore: move verify signature to atoma-utils * add trace --- Cargo.lock | 5 +- atoma-service/Cargo.toml | 1 - atoma-service/src/middleware.rs | 87 +-------------------------------- atoma-utils/Cargo.toml | 4 ++ atoma-utils/src/lib.rs | 85 ++++++++++++++++++++++++++++++++ 5 files changed, 95 insertions(+), 87 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 964da174..de99dedb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -587,7 +587,6 @@ dependencies = [ "clap", "config", "dotenv", - "fastcrypto 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "flume", "futures", "hex", @@ -657,7 +656,11 @@ name = "atoma-utils" version = "0.1.0" dependencies = [ "anyhow", + "axum", + "fastcrypto 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", + "sui-sdk", "tokio", + "tracing", ] [[package]] diff --git a/atoma-service/Cargo.toml b/atoma-service/Cargo.toml index e43ebf81..afadc7f0 100644 --- a/atoma-service/Cargo.toml +++ b/atoma-service/Cargo.toml @@ -20,7 +20,6 @@ blake2 = { workspace = true } clap = { workspace = true } config = { workspace = true } dotenv = { workspace = true } -fastcrypto = { workspace = true } flume = { workspace = true } futures = { workspace = true } hex = { workspace = true } diff --git a/atoma-service/src/middleware.rs b/atoma-service/src/middleware.rs index 90309079..4196adbe 100644 --- a/atoma-service/src/middleware.rs +++ b/atoma-service/src/middleware.rs @@ -8,6 +8,7 @@ use crate::{ server::AppState, }; use atoma_state::types::AtomaAtomaStateManagerEvent; +use atoma_utils::verify_signature; use axum::{ body::Body, extract::State, @@ -166,7 +167,7 @@ pub async fn signature_verification_middleware( .try_into() .expect("Invalid Blake2b hash length"); - utils::verify_signature(base64_signature, &body_blake2b_hash_bytes)?; + verify_signature(base64_signature, &body_blake2b_hash_bytes)?; let request_metadata = req_parts .extensions @@ -371,90 +372,6 @@ pub async fn verify_stack_permissions( pub(crate) mod utils { use super::*; - use fastcrypto::{ - ed25519::{Ed25519PublicKey, Ed25519Signature}, - secp256k1::{Secp256k1PublicKey, Secp256k1Signature}, - secp256r1::{Secp256r1PublicKey, Secp256r1Signature}, - traits::{ToFromBytes, VerifyingKey}, - }; - use sui_sdk::types::{crypto::SignatureScheme, digests::TransactionDigest}; - - /// Verifies the authenticity of a request by checking its signature against the provided hash. - /// - /// # Arguments - /// * `base64_signature` - A base64-encoded signature string that contains: - /// - The signature itself - /// - The public key - /// - The signature scheme used - /// * `body_hash` - A 32-byte Blake2b hash of the request body - /// - /// # Returns - /// * `Ok(())` if the signature is valid - /// * `Err(StatusCode)` if: - /// - The signature cannot be parsed (`BAD_REQUEST`) - /// - The public key is invalid (`BAD_REQUEST`) - /// - The signature scheme is unsupported (`BAD_REQUEST`) - /// - The signature verification fails (`UNAUTHORIZED`) - /// - /// # Supported Signature Schemes - /// - ED25519 - /// - Secp256k1 - /// - Secp256r1 - /// - /// # Security Note - /// This function is critical for ensuring request authenticity. It verifies that: - /// 1. The request was signed by the owner of the public key - /// 2. The request body hasn't been tampered with since signing - #[instrument(level = "trace", skip_all)] - pub(crate) fn verify_signature( - base64_signature: &str, - body_hash: &[u8; 32], - ) -> Result<(), StatusCode> { - let signature = Signature::from_str(base64_signature).map_err(|_| { - error!("Failed to parse signature"); - StatusCode::BAD_REQUEST - })?; - let signature_bytes = signature.signature_bytes(); - let public_key_bytes = signature.public_key_bytes(); - let signature_scheme = signature.scheme(); - let public_key = - PublicKey::try_from_bytes(signature_scheme, public_key_bytes).map_err(|e| { - error!("Failed to extract public key from bytes, with error: {e}"); - StatusCode::BAD_REQUEST - })?; - - match signature_scheme { - SignatureScheme::ED25519 => { - let public_key = Ed25519PublicKey::from_bytes(public_key.as_ref()).unwrap(); - let signature = Ed25519Signature::from_bytes(signature_bytes).unwrap(); - public_key.verify(body_hash, &signature).map_err(|_| { - error!("Failed to verify signature"); - StatusCode::UNAUTHORIZED - })?; - } - SignatureScheme::Secp256k1 => { - let public_key = Secp256k1PublicKey::from_bytes(public_key.as_ref()).unwrap(); - let signature = Secp256k1Signature::from_bytes(signature_bytes).unwrap(); - public_key.verify(body_hash, &signature).map_err(|_| { - error!("Failed to verify signature"); - StatusCode::UNAUTHORIZED - })?; - } - SignatureScheme::Secp256r1 => { - let public_key = Secp256r1PublicKey::from_bytes(public_key.as_ref()).unwrap(); - let signature = Secp256r1Signature::from_bytes(signature_bytes).unwrap(); - public_key.verify(body_hash, &signature).map_err(|_| { - error!("Failed to verify signature"); - StatusCode::UNAUTHORIZED - })?; - } - _ => { - error!("Currently unsupported signature scheme"); - return Err(StatusCode::BAD_REQUEST); - } - } - Ok(()) - } /// Queries the blockchain to retrieve compute units associated with a specific transaction. /// diff --git a/atoma-utils/Cargo.toml b/atoma-utils/Cargo.toml index 9144e323..776d56d0 100644 --- a/atoma-utils/Cargo.toml +++ b/atoma-utils/Cargo.toml @@ -6,4 +6,8 @@ license.workspace = true [dependencies] anyhow.workspace = true +axum.workspace = true +fastcrypto.workspace = true +sui-sdk = { workspace = true } tokio.workspace = true +tracing.workspace = true diff --git a/atoma-utils/src/lib.rs b/atoma-utils/src/lib.rs index e7fbed9a..bee2a9f3 100644 --- a/atoma-utils/src/lib.rs +++ b/atoma-utils/src/lib.rs @@ -1,5 +1,16 @@ +use std::str::FromStr; + use anyhow::{Context, Error, Result}; +use axum::http::StatusCode; +use fastcrypto::{ + ed25519::{Ed25519PublicKey, Ed25519Signature}, + secp256k1::{Secp256k1PublicKey, Secp256k1Signature}, + secp256r1::{Secp256r1PublicKey, Secp256r1Signature}, + traits::{ToFromBytes, VerifyingKey}, +}; +use sui_sdk::types::crypto::{PublicKey, Signature, SignatureScheme, SuiSignature}; use tokio::sync::watch; +use tracing::{error, instrument}; /// Spawns a task that will automatically trigger shutdown if it encounters an error /// @@ -42,6 +53,80 @@ where }) } +/// Verifies the authenticity of a request by checking its signature against the provided hash. +/// +/// # Arguments +/// * `base64_signature` - A base64-encoded signature string that contains: +/// - The signature itself +/// - The public key +/// - The signature scheme used +/// * `body_hash` - A 32-byte Blake2b hash of the request body +/// +/// # Returns +/// * `Ok(())` if the signature is valid +/// * `Err(StatusCode)` if: +/// - The signature cannot be parsed (`BAD_REQUEST`) +/// - The public key is invalid (`BAD_REQUEST`) +/// - The signature scheme is unsupported (`BAD_REQUEST`) +/// - The signature verification fails (`UNAUTHORIZED`) +/// +/// # Supported Signature Schemes +/// - ED25519 +/// - Secp256k1 +/// - Secp256r1 +/// +/// # Security Note +/// This function is critical for ensuring request authenticity. It verifies that: +/// 1. The request was signed by the owner of the public key +/// 2. The request body hasn't been tampered with since signing +#[instrument(level = "trace", skip_all)] +pub fn verify_signature(base64_signature: &str, body_hash: &[u8; 32]) -> Result<(), StatusCode> { + let signature = Signature::from_str(base64_signature).map_err(|_| { + error!("Failed to parse signature"); + StatusCode::BAD_REQUEST + })?; + let signature_bytes = signature.signature_bytes(); + let public_key_bytes = signature.public_key_bytes(); + let signature_scheme = signature.scheme(); + let public_key = + PublicKey::try_from_bytes(signature_scheme, public_key_bytes).map_err(|e| { + error!("Failed to extract public key from bytes, with error: {e}"); + StatusCode::BAD_REQUEST + })?; + + match signature_scheme { + SignatureScheme::ED25519 => { + let public_key = Ed25519PublicKey::from_bytes(public_key.as_ref()).unwrap(); + let signature = Ed25519Signature::from_bytes(signature_bytes).unwrap(); + public_key.verify(body_hash, &signature).map_err(|_| { + error!("Failed to verify signature"); + StatusCode::UNAUTHORIZED + })?; + } + SignatureScheme::Secp256k1 => { + let public_key = Secp256k1PublicKey::from_bytes(public_key.as_ref()).unwrap(); + let signature = Secp256k1Signature::from_bytes(signature_bytes).unwrap(); + public_key.verify(body_hash, &signature).map_err(|_| { + error!("Failed to verify signature"); + StatusCode::UNAUTHORIZED + })?; + } + SignatureScheme::Secp256r1 => { + let public_key = Secp256r1PublicKey::from_bytes(public_key.as_ref()).unwrap(); + let signature = Secp256r1Signature::from_bytes(signature_bytes).unwrap(); + public_key.verify(body_hash, &signature).map_err(|_| { + error!("Failed to verify signature"); + StatusCode::UNAUTHORIZED + })?; + } + _ => { + error!("Currently unsupported signature scheme"); + return Err(StatusCode::BAD_REQUEST); + } + } + Ok(()) +} + pub mod test { pub const POSTGRES_TEST_DB_URL: &str = "postgres://atoma:atoma@localhost:5432/atoma"; }