Skip to content

Commit 6bcbafc

Browse files
committed
Update dependencies and implement AWS KMS signing support
- Updated `Cargo.lock` to reflect new versions for several dependencies, including `alloy-consensus`, `alloy-eips`, and AWS-related packages. - Introduced `AwsKmsCredential` for AWS KMS signing, allowing integration of AWS KMS into the signing process. - Enhanced `SigningCredential` enum to support AWS KMS credentials. - Implemented error handling for AWS KMS signing errors in the `EngineError` enum. - Updated user operation signing logic to utilize AWS KMS for signing user operations. - Refactored HTTP extractors to support AWS KMS credentials extraction from headers. These changes improve the integration of AWS KMS for signing operations, enhancing the overall functionality and security of the application.
1 parent 509e657 commit 6bcbafc

File tree

14 files changed

+1021
-130
lines changed

14 files changed

+1021
-130
lines changed

Cargo.lock

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

aa-types/src/userop.rs

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use alloy::{
22
core::sol_types::SolValue,
3-
primitives::{Address, B256, Bytes, ChainId, U256, keccak256},
3+
primitives::{Address, B256, Bytes, ChainId, U256, address, keccak256},
44
rpc::types::{PackedUserOperation, UserOperation},
55
};
66
use serde::{Deserialize, Serialize};
@@ -13,6 +13,9 @@ pub enum VersionedUserOp {
1313
V0_7(PackedUserOperation),
1414
}
1515

16+
pub const ENTRYPOINT_ADDRESS_V0_6: Address = address!("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"); // v0.6
17+
pub const ENTRYPOINT_ADDRESS_V0_7: Address = address!("0x0000000071727De22E5E9d8BAf0edAc6f37da032"); // v0.7
18+
1619
/// Error type for UserOp operations
1720
#[derive(
1821
Debug,
@@ -182,3 +185,34 @@ pub fn compute_user_op_v07_hash(
182185
let final_hash = keccak256(&outer_encoded);
183186
Ok(final_hash)
184187
}
188+
189+
impl VersionedUserOp {
190+
pub fn hash(&self, chain_id: ChainId) -> Result<B256, UserOpError> {
191+
match self {
192+
VersionedUserOp::V0_6(op) => {
193+
compute_user_op_v06_hash(op, ENTRYPOINT_ADDRESS_V0_6, chain_id)
194+
}
195+
VersionedUserOp::V0_7(op) => {
196+
compute_user_op_v07_hash(op, ENTRYPOINT_ADDRESS_V0_7, chain_id)
197+
}
198+
}
199+
}
200+
201+
pub fn hash_with_custom_entrypoint(
202+
&self,
203+
chain_id: ChainId,
204+
entrypoint: Address,
205+
) -> Result<B256, UserOpError> {
206+
match self {
207+
VersionedUserOp::V0_6(op) => compute_user_op_v06_hash(op, entrypoint, chain_id),
208+
VersionedUserOp::V0_7(op) => compute_user_op_v07_hash(op, entrypoint, chain_id),
209+
}
210+
}
211+
212+
pub fn default_entrypoint(&self) -> Address {
213+
match self {
214+
VersionedUserOp::V0_6(_) => ENTRYPOINT_ADDRESS_V0_6,
215+
VersionedUserOp::V0_7(_) => ENTRYPOINT_ADDRESS_V0_7,
216+
}
217+
}
218+
}

core/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,7 @@ thirdweb-core = { version = "0.1.0", path = "../thirdweb-core" }
1919
uuid = { version = "1.17.0", features = ["v4"] }
2020
utoipa = { version = "5.4.0", features = ["preserve_order"] }
2121
serde_with = "3.13.0"
22+
alloy-signer-aws = { version = "1.0.23", features = ["eip712"] }
23+
aws-config = "1.8.2"
24+
aws-sdk-kms = "1.79.0"
25+
aws-credential-types = "1.2.4"

core/src/constants.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,3 @@ pub const DEFAULT_FACTORY_ADDRESS_V0_6: Address =
1111

1212
pub const DEFAULT_IMPLEMENTATION_ADDRESS_V0_6: Address =
1313
address!("0xf22175c80c6e074C171811C59C6c0087e2a6a346");
14-
15-
pub const ENTRYPOINT_ADDRESS_V0_6: Address = address!("0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789"); // v0.6
16-
17-
pub const ENTRYPOINT_ADDRESS_V0_7: Address = address!("0x0000000071727De22E5E9d8BAf0edAc6f37da032"); // v0.7

core/src/credentials.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,58 @@
1+
use alloy::primitives::ChainId;
2+
use alloy_signer_aws::AwsSigner;
3+
use aws_config::BehaviorVersion;
4+
use aws_credential_types::provider::future::ProvideCredentials as ProvideCredentialsFuture;
5+
use aws_sdk_kms::config::{Credentials, ProvideCredentials};
16
use serde::{Deserialize, Serialize};
27
use thirdweb_core::auth::ThirdwebAuth;
38
use thirdweb_core::iaw::AuthToken;
4-
use vault_types::enclave::auth::Auth;
9+
use vault_types::enclave::auth::Auth as VaultAuth;
10+
11+
use crate::error::EngineError;
512

613
#[derive(Debug, Clone, Serialize, Deserialize)]
714
pub enum SigningCredential {
8-
Vault(Auth),
9-
Iaw {
10-
auth_token: AuthToken,
11-
thirdweb_auth: ThirdwebAuth
15+
Vault(VaultAuth),
16+
Iaw {
17+
auth_token: AuthToken,
18+
thirdweb_auth: ThirdwebAuth,
1219
},
20+
AwsKms(AwsKmsCredential),
21+
}
22+
23+
#[derive(Debug, Clone, Serialize, Deserialize)]
24+
pub struct AwsKmsCredential {
25+
pub access_key_id: String,
26+
pub secret_access_key: String,
27+
pub key_id: String,
28+
pub region: String,
29+
}
30+
31+
impl ProvideCredentials for AwsKmsCredential {
32+
fn provide_credentials<'a>(&'a self) -> ProvideCredentialsFuture<'a>
33+
where
34+
Self: 'a,
35+
{
36+
let credentials = Credentials::new(
37+
self.access_key_id.clone(),
38+
self.secret_access_key.clone(),
39+
None,
40+
None,
41+
"engine-core",
42+
);
43+
ProvideCredentialsFuture::ready(Ok(credentials))
44+
}
45+
}
46+
47+
impl AwsKmsCredential {
48+
pub async fn get_signer(&self, chain_id: Option<ChainId>) -> Result<AwsSigner, EngineError> {
49+
let config = aws_config::defaults(BehaviorVersion::latest())
50+
.credentials_provider(self.clone())
51+
.load()
52+
.await;
53+
let client = aws_sdk_kms::Client::new(&config);
54+
55+
let signer = AwsSigner::new(client, self.key_id.clone(), chain_id).await?;
56+
Ok(signer)
57+
}
1358
}

core/src/error.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
1+
use std::fmt::Debug;
2+
13
use crate::defs::AddressDef;
24
use alloy::{
35
primitives::Address,
46
transports::{
57
RpcError as AlloyRpcError, TransportErrorKind, http::reqwest::header::InvalidHeaderValue,
68
},
79
};
10+
use alloy_signer_aws::AwsSignerError;
11+
use aws_sdk_kms::error::SdkError;
812
use schemars::JsonSchema;
913
use serde::{Deserialize, Serialize};
1014
use thirdweb_core::error::ThirdwebError;
@@ -242,11 +246,150 @@ pub enum EngineError {
242246
#[error("Thirdweb error: {message}")]
243247
ThirdwebError { message: String },
244248

249+
#[schema(title = "AWS KMS Error")]
250+
#[error(transparent)]
251+
#[serde(rename_all = "camelCase")]
252+
AwsKmsSignerError {
253+
#[serde(flatten)]
254+
error: SerialisableAwsSignerError,
255+
},
256+
245257
#[schema(title = "Engine Internal Error")]
246258
#[error("Internal error: {message}")]
247259
InternalError { message: String },
248260
}
249261

262+
#[derive(thiserror::Error, Debug, Serialize, Clone, Deserialize, utoipa::ToSchema)]
263+
#[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "type")]
264+
pub enum SerialisableAwsSdkError {
265+
/// The request failed during construction. It was not dispatched over the network.
266+
#[error("Construction failure: {message}")]
267+
ConstructionFailure { message: String },
268+
269+
/// The request failed due to a timeout. The request MAY have been sent and received.
270+
#[error("Timeout error: {message}")]
271+
TimeoutError { message: String },
272+
273+
/// The request failed during dispatch. An HTTP response was not received. The request MAY
274+
/// have been sent.
275+
#[error("Dispatch failure: {message}")]
276+
DispatchFailure { message: String },
277+
278+
/// A response was received but it was not parseable according the the protocol (for example
279+
/// the server hung up without sending a complete response)
280+
#[error("Response error: {message}")]
281+
ResponseError { message: String },
282+
283+
/// An error response was received from the service
284+
#[error("Service error: {message}")]
285+
ServiceError { message: String },
286+
287+
#[error("Other error: {message}")]
288+
Other { message: String },
289+
}
290+
291+
#[derive(Error, Debug, Serialize, Clone, Deserialize, utoipa::ToSchema)]
292+
#[serde(rename_all = "SCREAMING_SNAKE_CASE", tag = "type")]
293+
pub enum SerialisableAwsSignerError {
294+
/// Thrown when the AWS KMS API returns a signing error.
295+
#[error(transparent)]
296+
Sign {
297+
aws_sdk_error: SerialisableAwsSdkError,
298+
},
299+
300+
/// Thrown when the AWS KMS API returns an error.
301+
#[error(transparent)]
302+
GetPublicKey {
303+
aws_sdk_error: SerialisableAwsSdkError,
304+
},
305+
306+
/// [`ecdsa`] error.
307+
#[error("ECDSA error: {message}")]
308+
K256 { message: String },
309+
310+
/// [`spki`] error.
311+
#[error("SPKI error: {message}")]
312+
Spki { message: String },
313+
314+
/// [`hex`](mod@hex) error.
315+
#[error("Hex error: {message}")]
316+
Hex { message: String },
317+
318+
/// Thrown when the AWS KMS API returns a response without a signature.
319+
#[error("signature not found in response")]
320+
SignatureNotFound,
321+
322+
/// Thrown when the AWS KMS API returns a response without a public key.
323+
#[error("public key not found in response")]
324+
PublicKeyNotFound,
325+
326+
#[error("Unknown error: {message}")]
327+
Unknown { message: String },
328+
}
329+
330+
impl<T: Debug> From<SdkError<T>> for SerialisableAwsSdkError {
331+
fn from(err: SdkError<T>) -> Self {
332+
match err {
333+
SdkError::ConstructionFailure(err) => SerialisableAwsSdkError::ConstructionFailure {
334+
message: format!("{:?}", err),
335+
},
336+
SdkError::TimeoutError(err) => SerialisableAwsSdkError::TimeoutError {
337+
message: format!("{:?}", err),
338+
},
339+
SdkError::DispatchFailure(err) => SerialisableAwsSdkError::DispatchFailure {
340+
message: format!("{:?}", err),
341+
},
342+
SdkError::ResponseError(err) => SerialisableAwsSdkError::ResponseError {
343+
message: format!("{:?}", err),
344+
},
345+
SdkError::ServiceError(err) => SerialisableAwsSdkError::ServiceError {
346+
message: format!("{:?}", err),
347+
},
348+
_ => SerialisableAwsSdkError::Other {
349+
message: format!("{:?}", err),
350+
},
351+
}
352+
}
353+
}
354+
355+
impl From<AwsSignerError> for EngineError {
356+
fn from(err: AwsSignerError) -> Self {
357+
match err {
358+
AwsSignerError::Sign(err) => EngineError::AwsKmsSignerError {
359+
error: SerialisableAwsSignerError::Sign {
360+
aws_sdk_error: err.into(),
361+
},
362+
},
363+
AwsSignerError::GetPublicKey(err) => EngineError::AwsKmsSignerError {
364+
error: SerialisableAwsSignerError::GetPublicKey {
365+
aws_sdk_error: err.into(),
366+
},
367+
},
368+
AwsSignerError::K256(err) => EngineError::AwsKmsSignerError {
369+
error: SerialisableAwsSignerError::K256 {
370+
message: err.to_string(),
371+
},
372+
},
373+
AwsSignerError::Spki(err) => EngineError::AwsKmsSignerError {
374+
error: SerialisableAwsSignerError::Spki {
375+
message: err.to_string(),
376+
},
377+
},
378+
AwsSignerError::Hex(err) => EngineError::AwsKmsSignerError {
379+
error: SerialisableAwsSignerError::Hex {
380+
message: err.to_string(),
381+
},
382+
},
383+
AwsSignerError::SignatureNotFound => EngineError::AwsKmsSignerError {
384+
error: SerialisableAwsSignerError::SignatureNotFound,
385+
},
386+
AwsSignerError::PublicKeyNotFound => EngineError::AwsKmsSignerError {
387+
error: SerialisableAwsSignerError::PublicKeyNotFound,
388+
},
389+
}
390+
}
391+
}
392+
250393
impl From<vault_sdk::error::VaultError> for EngineError {
251394
fn from(err: vault_sdk::error::VaultError) -> Self {
252395
let message = match &err {

core/src/execution_options/aa.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
use crate::{
2-
constants::{DEFAULT_FACTORY_ADDRESS_V0_6, ENTRYPOINT_ADDRESS_V0_6},
3-
defs::AddressDef,
4-
error::EngineError,
1+
use crate::{constants::DEFAULT_FACTORY_ADDRESS_V0_6, defs::AddressDef, error::EngineError};
2+
use alloy::{
3+
hex::FromHex,
4+
primitives::{Address, Bytes},
55
};
6-
use alloy::{hex::FromHex, primitives::{Address, Bytes}};
6+
use engine_aa_types::{ENTRYPOINT_ADDRESS_V0_6, ENTRYPOINT_ADDRESS_V0_7};
77
use schemars::JsonSchema;
88
use serde::{Deserialize, Deserializer, Serialize};
99

10-
use crate::constants::{DEFAULT_FACTORY_ADDRESS_V0_7, ENTRYPOINT_ADDRESS_V0_7};
10+
use crate::constants::DEFAULT_FACTORY_ADDRESS_V0_7;
1111

1212
#[derive(Deserialize, Serialize, Debug, JsonSchema, Clone, Copy, utoipa::ToSchema)]
1313
pub enum EntrypointVersion {

0 commit comments

Comments
 (0)