Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
123 changes: 1 addition & 122 deletions tee-worker/omni-executor/rpc-server/src/detailed_error.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
use crate::error_code::{
EMAIL_SERVICE_ERROR_CODE, GAS_ESTIMATION_FAILED_CODE, INVALID_BACKEND_RESPONSE_CODE,
INVALID_USEROP_CODE, PASSKEY_ALREADY_EXISTS_CODE, PASSKEY_ATTESTATION_PARSE_ERROR_CODE,
PASSKEY_CHALLENGE_EXPIRED_CODE, PASSKEY_CHALLENGE_NOT_FOUND_CODE,
PASSKEY_CLIENT_DATA_PARSE_ERROR_CODE, PASSKEY_COUNTER_VALIDATION_FAILED_CODE,
PASSKEY_INVALID_CHALLENGE_CODE, PASSKEY_NOT_FOUND_CODE,
PASSKEY_ORIGIN_VERIFICATION_FAILED_CODE, PASSKEY_PARSE_ERROR_CODE, PASSKEY_REPLAY_ATTACK_CODE,
PASSKEY_RP_ID_MISMATCH_CODE, PASSKEY_SIGNATURE_INVALID_CODE,
PASSKEY_USER_VERIFICATION_FAILED_CODE, PUMPX_SERVICE_ERROR_CODE, SIGNER_SERVICE_ERROR_CODE,
INVALID_USEROP_CODE, PUMPX_SERVICE_ERROR_CODE, SIGNER_SERVICE_ERROR_CODE,
STORAGE_SERVICE_ERROR_CODE, WILDMETA_SERVICE_ERROR_CODE,
};
use jsonrpsee::types::{ErrorCode, ErrorObject, ErrorObjectOwned};
Expand Down Expand Up @@ -156,121 +150,6 @@ impl DetailedError {
pub fn gas_estimation_failed() -> Self {
Self::new(GAS_ESTIMATION_FAILED_CODE, "Gas estimaton failed")
}

// Passkey/WebAuthn error factory methods
pub fn passkey_challenge_not_found() -> Self {
Self::new(PASSKEY_CHALLENGE_NOT_FOUND_CODE, "Passkey challenge not found")
.with_reason("Challenge may have expired or was never created")
.with_suggestion("Request a new challenge using omni_requestPasskeyChallenge")
}

pub fn passkey_challenge_expired() -> Self {
Self::new(PASSKEY_CHALLENGE_EXPIRED_CODE, "Passkey challenge has expired")
.with_reason("Challenge exceeded its 5-minute validity period")
.with_suggestion("Request a new challenge using omni_requestPasskeyChallenge")
}

pub fn passkey_invalid_challenge(reason: &str) -> Self {
Self::new(PASSKEY_INVALID_CHALLENGE_CODE, "Invalid passkey challenge")
.with_reason(reason)
.with_suggestion(
"Ensure the challenge matches the one received from omni_requestPasskeyChallenge",
)
}

pub fn passkey_not_found(credential_id: &str) -> Self {
Self::new(PASSKEY_NOT_FOUND_CODE, "Passkey not found")
.with_field("credential_id")
.with_received(credential_id)
.with_reason("No passkey registered for this account and credential ID")
.with_suggestion("Register a new passkey using omni_attachPasskey")
}

pub fn passkey_signature_invalid(reason: &str) -> Self {
Self::new(PASSKEY_SIGNATURE_INVALID_CODE, "Passkey signature verification failed")
.with_reason(reason)
.with_suggestion("Ensure the passkey signature is correctly generated and matches the registered public key")
}

pub fn passkey_parse_error(field: &str, error: &str) -> Self {
Self::new(PASSKEY_PARSE_ERROR_CODE, "Failed to parse passkey data")
.with_field(field)
.with_reason(error)
.with_suggestion("Check the passkey data format and encoding")
}

pub fn passkey_rp_id_mismatch(expected_rp_id: &str, client_id: &str) -> Self {
Self::new(PASSKEY_RP_ID_MISMATCH_CODE, "Passkey RP ID validation failed")
.with_field("rp_id")
.with_expected(expected_rp_id)
.with_reason(format!(
"RP ID hash in authenticator data does not match expected RP ID for client '{}'",
client_id
))
.with_suggestion("Ensure the passkey was created for the correct relying party domain")
}

pub fn passkey_replay_attack(counter: u32) -> Self {
Self::new(PASSKEY_REPLAY_ATTACK_CODE, "Replay attack detected")
.with_field("signature_counter")
.with_received(counter.to_string())
.with_reason(
"Signature counter is not greater than stored value - possible replay attack",
)
.with_suggestion("This authentication request may have been captured and replayed by an attacker. Contact support if you believe this is an error.")
}

pub fn passkey_counter_validation_failed() -> Self {
Self::new(
PASSKEY_COUNTER_VALIDATION_FAILED_CODE,
"Passkey counter validation failed",
)
.with_reason("Authenticator may have been cloned or downgraded")
.with_suggestion("The authenticator returned a zero counter when a non-zero counter was expected. This may indicate device cloning or tampering.")
}

pub fn passkey_user_verification_failed(flag_type: &str) -> Self {
Self::new(PASSKEY_USER_VERIFICATION_FAILED_CODE, "Passkey user verification failed")
.with_field(flag_type)
.with_reason(format!("{} flag not set in authenticator data", flag_type))
.with_suggestion(
"Ensure user presence and verification are enabled on the authenticator",
)
}

pub fn passkey_attestation_parse_error(error: &str) -> Self {
Self::new(
PASSKEY_ATTESTATION_PARSE_ERROR_CODE,
"Failed to parse passkey attestation object",
)
.with_reason(error)
.with_suggestion(
"Ensure the attestation object is properly CBOR-encoded and base64url-encoded",
)
}

pub fn passkey_already_exists(credential_id: &str) -> Self {
Self::new(PASSKEY_ALREADY_EXISTS_CODE, "Passkey already registered")
.with_field("credential_id")
.with_received(credential_id)
.with_reason("A passkey with this credential ID already exists for this account")
.with_suggestion("Use the existing passkey or remove it before registering a new one")
}

pub fn passkey_origin_verification_failed(expected_origin: &str) -> Self {
Self::new(PASSKEY_ORIGIN_VERIFICATION_FAILED_CODE, "Origin verification failed")
.with_field("client_data_json")
.with_expected(format!("Origin: {}", expected_origin))
.with_reason("Origin in client data does not match expected value")
.with_suggestion("Ensure the WebAuthn ceremony is initiated from the correct origin")
}

pub fn passkey_client_data_parse_error(error: &str) -> Self {
Self::new(PASSKEY_CLIENT_DATA_PARSE_ERROR_CODE, "Failed to parse client data JSON")
.with_field("client_data_json")
.with_reason(error)
.with_suggestion("Ensure the client data is properly base64url-encoded JSON")
}
}

impl From<DetailedError> for ErrorObjectOwned {
Expand Down
16 changes: 0 additions & 16 deletions tee-worker/omni-executor/rpc-server/src/error_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,6 @@ pub const INVALID_RPC_EXTENSION: i32 = -32002;
pub const AUTH_VERIFICATION_FAILED_CODE: i32 = -32003;
pub const GAS_ESTIMATION_FAILED_CODE: i32 = -32005;

// Passkey/WebAuthn Error Codes (-32140 to -32159)
pub const PASSKEY_CHALLENGE_NOT_FOUND_CODE: i32 = -32140;
pub const PASSKEY_CHALLENGE_EXPIRED_CODE: i32 = -32141;
pub const PASSKEY_INVALID_CHALLENGE_CODE: i32 = -32142;
pub const PASSKEY_NOT_FOUND_CODE: i32 = -32143;
pub const PASSKEY_SIGNATURE_INVALID_CODE: i32 = -32144;
pub const PASSKEY_PARSE_ERROR_CODE: i32 = -32145;
pub const PASSKEY_RP_ID_MISMATCH_CODE: i32 = -32146;
pub const PASSKEY_REPLAY_ATTACK_CODE: i32 = -32147;
pub const PASSKEY_COUNTER_VALIDATION_FAILED_CODE: i32 = -32148;
pub const PASSKEY_USER_VERIFICATION_FAILED_CODE: i32 = -32149;
pub const PASSKEY_ATTESTATION_PARSE_ERROR_CODE: i32 = -32150;
pub const PASSKEY_ALREADY_EXISTS_CODE: i32 = -32151;
pub const PASSKEY_ORIGIN_VERIFICATION_FAILED_CODE: i32 = -32152;
pub const PASSKEY_CLIENT_DATA_PARSE_ERROR_CODE: i32 = -32153;

// Error when calling external services (-32160 to -32179)
pub const SIGNER_SERVICE_ERROR_CODE: i32 = -32160;
pub const PUMPX_SERVICE_ERROR_CODE: i32 = -32161;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
use crate::{
detailed_error::DetailedError, error_code::AUTH_VERIFICATION_FAILED_CODE, server::RpcContext,
detailed_error::DetailedError, server::RpcContext, utils::types::RpcResultExt,
utils::validation::parse_rpc_params, verify_auth::verify_auth, Deserialize, Serialize,
};

use executor_core::intent_executor::IntentExecutor;
use executor_crypto::passkey::{AttestationResult, PasskeyVerifier};
use executor_primitives::{to_omni_auth, utils::hex::hex_encode, UserAuth, UserId};
use executor_storage::{
PasskeyChallengeError, PasskeyChallengeStorage, PasskeyError, PasskeyStorage,
};
use executor_storage::{PasskeyChallengeError, PasskeyChallengeStorage, PasskeyStorage};
use heima_primitives::Identity;
use jsonrpsee::{types::ErrorObject, RpcModule};
use tracing::*;
Expand Down Expand Up @@ -49,16 +47,11 @@ pub fn register_attach_passkey<CrossChainIntentExecutor: IntentExecutor + Send +
.to_rpc_error());
}

let identity = Identity::try_from(params.user_id.clone()).map_err(|_| {
error!("Invalid existing user ID format");
DetailedError::parse_error("Invalid user ID format").to_rpc_error()
})?;
let identity = Identity::try_from(params.user_id.clone())
.map_err_parse("Invalid user ID format")?;

let auth = to_omni_auth(&params.user_auth, &params.user_id, &params.client_id)
.map_err(|e| {
error!("Failed to convert to OmniAuth: {:?}", e);
DetailedError::parse_error("Failed to convert to OmniAuth").to_rpc_error()
})?;
.map_err_parse("Failed to convert to OmniAuth")?;

verify_auth(ctx.clone(), &auth).await.map_err(|e| {
error!("Failed to verify existing user authentication: {:?}", e);
Expand Down Expand Up @@ -101,73 +94,17 @@ pub fn register_attach_passkey<CrossChainIntentExecutor: IntentExecutor + Send +
})
},
)
.map_err(|e| {
error!("Client data verification failed during passkey attachment: {:?}", e);
match e {
executor_crypto::passkey::PasskeyError::ChallengeVerificationFailed => {
DetailedError::passkey_invalid_challenge(
"Challenge mismatch, expired, or not found",
)
},
executor_crypto::passkey::PasskeyError::OriginVerificationFailed => {
DetailedError::passkey_origin_verification_failed(expected_origin)
},
executor_crypto::passkey::PasskeyError::AttestationParseError(err) => {
DetailedError::passkey_client_data_parse_error(&err)
},
_ => DetailedError::new(
AUTH_VERIFICATION_FAILED_CODE,
"Client data verification failed",
)
.with_field("client_data_json")
.with_reason(format!("Verification error: {:?}", e)),
}
.to_rpc_error()
})?;
.map_err_internal("Client data verification failed")?;

let AttestationResult { credential_id, public_key } =
PasskeyVerifier::verify_attestation(&params.attestation_object).map_err(|e| {
error!(
"WebAuthn attestation verification failed during passkey attachment: {:?}",
e
);
match e {
executor_crypto::passkey::PasskeyError::AttestationParseError(err) => {
DetailedError::passkey_attestation_parse_error(&err)
},
_ => DetailedError::new(
AUTH_VERIFICATION_FAILED_CODE,
"Attestation verification failed",
)
.with_field("attestation_object")
.with_reason(format!("Verification error: {:?}", e)),
}
.to_rpc_error()
})?;
PasskeyVerifier::verify_attestation(&params.attestation_object)
.map_err_parse("Attestation verification failed")?;

let public_key_sec1_bytes = public_key.verifying_key.to_sec1_bytes();
let passkey_storage = PasskeyStorage::new(ctx.storage_db.clone());
passkey_storage
.add_passkey(&omni_account, &credential_id, &public_key_sec1_bytes)
.map_err(|e| {
error!(
"Failed to attach passkey to omni_account {}: {:?}",
hex_encode(omni_account.as_ref()),
e
);
match e {
PasskeyError::DuplicatePasskey => {
DetailedError::passkey_already_exists(&credential_id)
},
PasskeyError::StorageError => {
DetailedError::storage_service_error("passkey attachment")
},
_ => DetailedError::internal_error("Failed to attach passkey")
.with_reason(format!("Storage error: {:?}", e))
.with_suggestion("Please try again later"),
}
.to_rpc_error()
})?;
.map_err_internal("Failed to attach passkey")?;

Ok::<AttachPasskeyResponse, ErrorObject>(AttachPasskeyResponse {
success: true,
Expand Down
Loading