diff --git a/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs b/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs index 3007a659c29794..b22b8123663122 100644 --- a/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs +++ b/crates/sui-e2e-tests/tests/passkey_e2e_tests.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use fastcrypto::traits::ToFromBytes; use p256::pkcs8::DecodePublicKey; -use passkey_authenticator::{Authenticator, UserValidationMethod}; +use passkey_authenticator::Authenticator; use passkey_client::Client; use passkey_types::{ ctap2::Aaguid, @@ -16,13 +16,11 @@ use passkey_types::{ Bytes, Passkey, }; use shared_crypto::intent::{Intent, IntentMessage}; -use std::net::SocketAddr; -use sui_core::authority_client::AuthorityAPI; use sui_macros::sim_test; use sui_test_transaction_builder::TestTransactionBuilder; use sui_types::crypto::Signature; +use sui_types::error::SuiError; use sui_types::error::UserInputError; -use sui_types::error::{SuiError, SuiResult}; use sui_types::signature::GenericSignature; use sui_types::transaction::Transaction; use sui_types::{ diff --git a/crates/sui-e2e-tests/tests/passkey_session_e2e_tests.rs b/crates/sui-e2e-tests/tests/passkey_session_e2e_tests.rs index 34110a5f64d65d..f15ca2d5f92fff 100644 --- a/crates/sui-e2e-tests/tests/passkey_session_e2e_tests.rs +++ b/crates/sui-e2e-tests/tests/passkey_session_e2e_tests.rs @@ -2,11 +2,10 @@ // SPDX-License-Identifier: Apache-2.0 use fastcrypto::{ ed25519::Ed25519KeyPair, - secp256r1::Secp256r1KeyPair, traits::{KeyPair, ToFromBytes}, }; use p256::pkcs8::DecodePublicKey; -use passkey_authenticator::{Authenticator, UserValidationMethod}; +use passkey_authenticator::Authenticator; use passkey_client::Client; use passkey_types::{ ctap2::Aaguid, @@ -20,11 +19,11 @@ use passkey_types::{ Bytes, Passkey, }; use rand::{rngs::StdRng, SeedableRng}; -use shared_crypto::intent::{Intent, IntentMessage, PersonalMessage}; +use shared_crypto::intent::{Intent, IntentMessage}; use sui_macros::sim_test; use sui_test_transaction_builder::TestTransactionBuilder; +use sui_types::error::SuiError; use sui_types::error::UserInputError; -use sui_types::error::{SuiError, SuiResult}; use sui_types::signature::GenericSignature; use sui_types::transaction::Transaction; use sui_types::{ @@ -34,7 +33,7 @@ use sui_types::{ }; use sui_types::{ crypto::{get_key_pair, Signature}, - passkey_session_authenticator::{PasskeySessionAuthenticator, RawPasskeySessionAuthenticator}, + passkey_session_authenticator::RawPasskeySessionAuthenticator, }; use test_cluster::TestCluster; use test_cluster::TestClusterBuilder; @@ -62,8 +61,7 @@ async fn make_passkey_session_tx( }; let response = - create_credential_and_commit_ephemeral_pk(test_cluster, None, register_msg, false, false) - .await; + create_credential_and_commit_ephemeral_pk(test_cluster, None, register_msg).await; let ephemeral_signature = Signature::new_secure(&response.intent_msg, &eph_kp); let passkey_session_authenticator = RawPasskeySessionAuthenticator { @@ -71,21 +69,16 @@ async fn make_passkey_session_tx( client_data_json: response.client_data_json, register_signature: if wrong_register_sig { let mut fake_signature = response.user_sig_bytes.clone(); - fake_signature[2] = fake_signature[2] + 1; + fake_signature[2] += 1; Signature::from_bytes(&fake_signature).unwrap() } else { Signature::from_bytes(&response.user_sig_bytes).unwrap() }, max_epoch, ephemeral_signature: if wrong_ephemeral_sig { - let fake_kp = Secp256r1KeyPair::generate(&mut StdRng::from_seed([0u8; 32])); - let intent_msg = IntentMessage::new( - Intent::personal_message(), - PersonalMessage { - message: "random".as_bytes().to_vec(), - }, - ); - Signature::new_secure(&intent_msg, &fake_kp) + let mut fake_signature = ephemeral_signature.as_bytes().to_vec().clone(); + fake_signature[2] += 1; + Signature::from_bytes(&fake_signature).unwrap() } else { ephemeral_signature }, @@ -102,8 +95,6 @@ async fn create_credential_and_commit_ephemeral_pk( test_cluster: &TestCluster, sender: Option, passkey_challenge: Vec, - change_intent: bool, - change_tx: bool, ) -> PasskeyResponse { // set up authenticator and client let my_aaguid = Aaguid::new_empty(); @@ -158,7 +149,8 @@ async fn create_credential_and_commit_ephemeral_pk( let prefix = if y.unwrap()[31] % 2 == 0 { 0x02 } else { 0x03 }; let mut pk_bytes = vec![prefix]; pk_bytes.extend_from_slice(x.unwrap()); - let pk = PublicKey::try_from_bytes(SignatureScheme::PasskeyAuthenticator, &pk_bytes).unwrap(); + let pk = + PublicKey::try_from_bytes(SignatureScheme::PasskeySessionAuthenticator, &pk_bytes).unwrap(); // Request a signature from passkey with challenge set to passkey_digest. let credential_request = CredentialRequestOptions { @@ -226,7 +218,7 @@ async fn test_passkey_session_feature_deny() { let test_cluster = TestClusterBuilder::new().build().await; let kp: Ed25519KeyPair = get_key_pair().1; - let max_epoch = 2 as u64; + let max_epoch = 2_u64; let tx = make_passkey_session_tx(&test_cluster, kp, max_epoch, None, false, false).await; let err = execute_tx(tx, &test_cluster).await.unwrap_err(); assert!(matches!( @@ -246,8 +238,12 @@ async fn test_passkey_authenticator_scenarios() { .build() .await; + let _guard = ProtocolConfig::apply_overrides_for_testing(|_, mut config| { + config.set_zklogin_max_epoch_upper_bound_delta_for_testing(Some(1)); + config + }); let kp = Ed25519KeyPair::generate(&mut StdRng::from_seed([0u8; 32])); - let max_epoch = 1 as u64; + let max_epoch = 1_u64; // case 1: sign a tx with passkey ephemeral sig and register sig, passes let tx = make_passkey_session_tx(&test_cluster, kp.copy(), max_epoch, None, false, false).await; @@ -303,26 +299,27 @@ async fn test_passkey_authenticator_scenarios() { // case 5: invalid ephemeral signature fails to verify. let tx = make_passkey_session_tx(&test_cluster, kp.copy(), max_epoch, None, false, true).await; let res = execute_tx(tx, &test_cluster).await; - println!("res: {:?}", res); assert!(matches!( res, Err(SuiError::InvalidSignature { error }) if error == "Fails to verify ephemeral sig" )); - // // case 6: advance 2 epochs, the ephermal sig expires, fails to verify - // test_cluster.trigger_reconfiguration().await; - // test_cluster.trigger_reconfiguration().await; + // case 6: advance 2 epochs, the ephermal sig expires, fails to verify + test_cluster.trigger_reconfiguration().await; + test_cluster.trigger_reconfiguration().await; - // let tx = make_passkey_session_tx(&test_cluster, kp.copy(), max_epoch, None, false, false).await; - // let res = execute_tx(tx, &test_cluster).await; - // assert!(matches!( - // res, - // Err(SuiError::InvalidSignature { error }) if error == "Passkey session max epoch too large 2, current epoch 2, max accepted: 1" - // )); - // // case 4: max_epoch bound delta = 1, but max epoch - current epoch > 1, too large, fails - // let _guard = ProtocolConfig::apply_overrides_for_testing(|_, mut config| { - // config.set_passkey_session_max_epoch_upper_bound_delta_for_testing(Some(1)); - // config - // }); + let tx = make_passkey_session_tx(&test_cluster, kp.copy(), max_epoch, None, false, false).await; + let res = execute_tx(tx, &test_cluster).await; + assert!(matches!( + res, + Err(SuiError::InvalidSignature { error }) if error == "Passkey session expired at epoch 1, current epoch 2" + )); + // case 4: max_epoch bound delta = 1, but max epoch (10) - current epoch (2) > 1, too large, fails + let tx = make_passkey_session_tx(&test_cluster, kp.copy(), 10, None, false, false).await; + let res = execute_tx(tx, &test_cluster).await; + assert!(matches!( + res, + Err(SuiError::InvalidSignature { error }) if error == "Passkey session max epoch too large 10, current epoch 2, max accepted: 3" + )); } #[sim_test] diff --git a/crates/sui-e2e-tests/tests/passkey_util.rs b/crates/sui-e2e-tests/tests/passkey_util.rs index 2403103e9f042d..a6062b61944fda 100644 --- a/crates/sui-e2e-tests/tests/passkey_util.rs +++ b/crates/sui-e2e-tests/tests/passkey_util.rs @@ -1,39 +1,13 @@ // Copyright (c) Mysten Labs, Inc. // SPDX-License-Identifier: Apache-2.0 -use fastcrypto::traits::ToFromBytes; -use p256::pkcs8::DecodePublicKey; -use passkey_authenticator::{Authenticator, UserValidationMethod}; -use passkey_client::Client; -use passkey_types::{ - ctap2::Aaguid, - rand::random_vec, - webauthn::{ - AttestationConveyancePreference, CredentialCreationOptions, CredentialRequestOptions, - PublicKeyCredentialCreationOptions, PublicKeyCredentialParameters, - PublicKeyCredentialRequestOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialType, - PublicKeyCredentialUserEntity, UserVerificationRequirement, - }, - Bytes, Passkey, -}; -use shared_crypto::intent::{Intent, IntentMessage}; +use passkey_authenticator::UserValidationMethod; +use shared_crypto::intent::IntentMessage; use std::net::SocketAddr; use sui_core::authority_client::AuthorityAPI; -use sui_macros::sim_test; -use sui_test_transaction_builder::TestTransactionBuilder; -use sui_types::crypto::Signature; -use sui_types::error::UserInputError; -use sui_types::error::{SuiError, SuiResult}; -use sui_types::signature::GenericSignature; +use sui_types::base_types::SuiAddress; +use sui_types::error::SuiResult; use sui_types::transaction::Transaction; -use sui_types::{ - base_types::SuiAddress, - crypto::{PublicKey, SignatureScheme}, - passkey_authenticator::{to_signing_message, PasskeyAuthenticator}, - transaction::TransactionData, -}; use test_cluster::TestCluster; -use test_cluster::TestClusterBuilder; -use url::Url; /// Helper struct to initialize passkey client. pub struct MyUserValidationMethod {} diff --git a/crates/sui-protocol-config/src/lib.rs b/crates/sui-protocol-config/src/lib.rs index 6b7ca55750bc90..125d1f4c93797b 100644 --- a/crates/sui-protocol-config/src/lib.rs +++ b/crates/sui-protocol-config/src/lib.rs @@ -537,10 +537,6 @@ struct FeatureFlags { #[serde(skip_serializing_if = "is_false")] passkey_session_auth: bool, - // Set the upper bound allowed for max_epoch in passkey session signature. - #[serde(skip_serializing_if = "Option::is_none")] - passkey_session_max_epoch_upper_bound_delta: Option, - // Use AuthorityCapabilitiesV2 #[serde(skip_serializing_if = "is_false")] authority_capabilities_v2: bool, @@ -3142,14 +3138,6 @@ impl ProtocolConfig { self.feature_flags.passkey_session_auth = val } - pub fn set_passkey_session_max_epoch_upper_bound_delta_for_testing( - &mut self, - val: Option, - ) { - self.feature_flags - .passkey_session_max_epoch_upper_bound_delta = val - } - pub fn set_consensus_distributed_vote_scoring_strategy_for_testing(&mut self, val: bool) { self.feature_flags .consensus_distributed_vote_scoring_strategy = val; diff --git a/crates/sui-rosetta/src/types.rs b/crates/sui-rosetta/src/types.rs index 2d499a494767f4..698035a38ecf13 100644 --- a/crates/sui-rosetta/src/types.rs +++ b/crates/sui-rosetta/src/types.rs @@ -393,6 +393,10 @@ impl From for PublicKey { hex_bytes: Hex::from_bytes(&k.0), curve_type: CurveType::Secp256r1, }, + SuiPublicKey::PasskeySession(k) => PublicKey { + hex_bytes: Hex::from_bytes(&k.0), + curve_type: CurveType::Secp256r1, + }, } } } diff --git a/crates/sui-types/src/crypto.rs b/crates/sui-types/src/crypto.rs index 57caeaf2b59f12..1f445f29bbed24 100644 --- a/crates/sui-types/src/crypto.rs +++ b/crates/sui-types/src/crypto.rs @@ -267,6 +267,7 @@ pub enum PublicKey { Secp256r1(Secp256r1PublicKeyAsBytes), ZkLogin(ZkLoginPublicIdentifier), Passkey(Secp256r1PublicKeyAsBytes), + PasskeySession(Secp256r1PublicKeyAsBytes), } /// A wrapper struct to retrofit in [enum PublicKey] for zkLogin. @@ -294,6 +295,7 @@ impl AsRef<[u8]> for PublicKey { PublicKey::Secp256r1(pk) => &pk.0, PublicKey::ZkLogin(z) => &z.0, PublicKey::Passkey(pk) => &pk.0, + PublicKey::PasskeySession(pk) => &pk.0, } } } @@ -331,6 +333,11 @@ impl EncodeDecodeBase64 for PublicKey { FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), )?)?; Ok(PublicKey::Passkey((&pk).into())) + } else if x == &SignatureScheme::PasskeySessionAuthenticator.flag() { + let pk = Secp256r1PublicKey::from_bytes(bytes.get(1..).ok_or( + FastCryptoError::InputLengthWrong(Secp256r1PublicKey::LENGTH + 1), + )?)?; + Ok(PublicKey::PasskeySession((&pk).into())) } else { Err(FastCryptoError::InvalidInput) } @@ -359,8 +366,10 @@ impl PublicKey { SignatureScheme::Secp256r1 => Ok(PublicKey::Secp256r1( (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), )), - SignatureScheme::PasskeyAuthenticator - | SignatureScheme::PasskeySessionAuthenticator => Ok(PublicKey::Passkey( + SignatureScheme::PasskeyAuthenticator => Ok(PublicKey::Passkey( + (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), + )), + SignatureScheme::PasskeySessionAuthenticator => Ok(PublicKey::PasskeySession( (&Secp256r1PublicKey::from_bytes(key_bytes)?).into(), )), _ => Err(eyre!("Unsupported curve")), @@ -374,6 +383,7 @@ impl PublicKey { PublicKey::Secp256r1(_) => Secp256r1SuiSignature::SCHEME, PublicKey::ZkLogin(_) => SignatureScheme::ZkLoginAuthenticator, PublicKey::Passkey(_) => SignatureScheme::PasskeyAuthenticator, + PublicKey::PasskeySession(_) => SignatureScheme::PasskeySessionAuthenticator, } } diff --git a/crates/sui-types/src/passkey_session_authenticator.rs b/crates/sui-types/src/passkey_session_authenticator.rs index d8852e4dbc9633..6f86d3a17062e8 100644 --- a/crates/sui-types/src/passkey_session_authenticator.rs +++ b/crates/sui-types/src/passkey_session_authenticator.rs @@ -171,7 +171,7 @@ impl Serialize for PasskeySessionAuthenticator { impl PasskeySessionAuthenticator { /// Returns the public key of the passkey authenticator. pub fn get_pk(&self) -> SuiResult { - Ok(PublicKey::Passkey((&self.passkey_pk).into())) + Ok(PublicKey::PasskeySession((&self.passkey_pk).into())) } } @@ -200,6 +200,12 @@ impl AuthenticatorTrait for PasskeySessionAuthenticator { ) -> SuiResult { // the checks here ensure that `current_epoch + passkey_session_max_epoch_upper_bound_delta >= self.max_epoch >= current_epoch`. // 1. if the config for upper bound is set, ensure that the max epoch in signature is not larger than epoch + upper_bound. + println!( + "tktk max_epoch_upper_bound_delta: {:?}", + max_epoch_upper_bound_delta + ); + println!("tktk self.max_epoch: {:?}", self.max_epoch); + println!("tktk epoch: {:?}", epoch); if let Some(delta) = max_epoch_upper_bound_delta { let max_epoch_upper_bound = epoch.checked_add(delta).ok_or(SuiError::InvalidSignature { diff --git a/crates/sui-types/src/unit_tests/passkey_session_authenticator_test.rs b/crates/sui-types/src/unit_tests/passkey_session_authenticator_test.rs index 53b177423e962e..a6440832dc1ee4 100644 --- a/crates/sui-types/src/unit_tests/passkey_session_authenticator_test.rs +++ b/crates/sui-types/src/unit_tests/passkey_session_authenticator_test.rs @@ -13,33 +13,20 @@ use crate::{ error::SuiError, object::Object, signature::GenericSignature, - signature_verification::VerifiedDigestCache, transaction::{TransactionData, TEST_ONLY_GAS_UNIT_FOR_TRANSFER}, }; use fastcrypto::ed25519::Ed25519KeyPair; -use std::sync::Arc; +use passkey_types::webauthn::{CredentialRequestOptions, PublicKeyCredentialRequestOptions}; use fastcrypto::traits::KeyPair; use fastcrypto::traits::ToFromBytes; use p256::pkcs8::DecodePublicKey; +use passkey_types::webauthn::CredentialCreationOptions; use crate::passkey_authenticator::passkey_authenticator_test::make_credential_creation_option; -use passkey_authenticator::{Authenticator, UserValidationMethod}; +use passkey_authenticator::Authenticator; use passkey_client::Client; -use passkey_types::{ - ctap2::Aaguid, - rand::random_vec, - webauthn::{ - AttestationConveyancePreference, CredentialCreationOptions, CredentialRequestOptions, - PublicKeyCredentialCreationOptions, PublicKeyCredentialParameters, - PublicKeyCredentialRequestOptions, PublicKeyCredentialRpEntity, PublicKeyCredentialType, - PublicKeyCredentialUserEntity, UserVerificationRequirement, - }, - Bytes, Passkey, -}; -use rand::rngs::StdRng; -use rand::SeedableRng; -use shared_crypto::intent::PersonalMessage; +use passkey_types::{ctap2::Aaguid, webauthn::UserVerificationRequirement, Bytes, Passkey}; use shared_crypto::intent::{Intent, IntentMessage}; use url::Url; /// Response with fields from passkey authentication.