diff --git a/aptos-move/vm-genesis/src/lib.rs b/aptos-move/vm-genesis/src/lib.rs index 23ad6125e7d07..2ebde5677bfb4 100644 --- a/aptos-move/vm-genesis/src/lib.rs +++ b/aptos-move/vm-genesis/src/lib.rs @@ -22,7 +22,8 @@ use aptos_types::{ contract_event::{ContractEvent, ContractEventV1}, executable::ModulePath, jwks::{ - patch::{PatchJWKMoveStruct, PatchUpsertJWK}, + jwk::{JWKMoveStruct, JWK}, + patch::{IssuerJWK, PatchJWKMoveStruct, PatchUpsertJWK}, secure_test_rsa_jwk, }, keyless::{ @@ -109,6 +110,8 @@ pub struct GenesisConfiguration { pub initial_features_override: Option, pub randomness_config_override: Option, pub jwk_consensus_config_override: Option, + pub initial_jwks: Vec, + pub keyless_groth16_vk_override: Option, } pub static GENESIS_KEYPAIR: Lazy<(Ed25519PrivateKey, Ed25519PublicKey)> = Lazy::new(|| { @@ -304,7 +307,13 @@ pub fn encode_genesis_change_set( .unwrap_or_else(OnChainJWKConsensusConfig::default_for_genesis); initialize_jwk_consensus_config(&mut session, &module_storage, &jwk_consensus_config); initialize_jwks_resources(&mut session, &module_storage); - initialize_keyless_accounts(&mut session, &module_storage, chain_id); + initialize_keyless_accounts( + &mut session, + &module_storage, + chain_id, + genesis_config.initial_jwks.clone(), + genesis_config.keyless_groth16_vk_override.clone(), + ); set_genesis_end(&mut session, &module_storage); // Reconfiguration should happen after all on-chain invocations. @@ -676,6 +685,8 @@ fn initialize_keyless_accounts( session: &mut SessionExt, module_storage: &impl AptosModuleStorage, chain_id: ChainId, + mut initial_jwks: Vec, + vk_override: Option, ) { let config = keyless::Configuration::new_for_devnet(); exec_function( @@ -690,7 +701,8 @@ fn initialize_keyless_accounts( ]), ); if !chain_id.is_mainnet() { - let vk = Groth16VerificationKey::from(&*DEVNET_VERIFICATION_KEY); + let vk = + vk_override.unwrap_or_else(|| Groth16VerificationKey::from(&*DEVNET_VERIFICATION_KEY)); exec_function( session, module_storage, @@ -703,11 +715,24 @@ fn initialize_keyless_accounts( ]), ); - let patch: PatchJWKMoveStruct = PatchUpsertJWK { + let additional_jwk_patch = IssuerJWK { issuer: get_sample_iss(), - jwk: secure_test_rsa_jwk().into(), - } - .into(); + jwk: JWK::RSA(secure_test_rsa_jwk()), + }; + initial_jwks.push(additional_jwk_patch); + + let jwk_patches: Vec = initial_jwks + .into_iter() + .map(|issuer_jwk| { + let IssuerJWK { issuer, jwk } = issuer_jwk; + let upsert_patch = PatchUpsertJWK { + issuer, + jwk: JWKMoveStruct::from(jwk), + }; + PatchJWKMoveStruct::from(upsert_patch) + }) + .collect(); + exec_function( session, module_storage, @@ -716,7 +741,7 @@ fn initialize_keyless_accounts( vec![], serialize_values(&vec![ MoveValue::Signer(CORE_CODE_ADDRESS), - MoveValue::Vector(vec![patch.as_move_value()]), + jwk_patches.as_move_value(), ]), ); } @@ -1229,6 +1254,8 @@ pub fn generate_test_genesis( initial_features_override: None, randomness_config_override: None, jwk_consensus_config_override: None, + initial_jwks: vec![], + keyless_groth16_vk_override: None, }, &OnChainConsensusConfig::default_for_genesis(), &OnChainExecutionConfig::default_for_genesis(), @@ -1279,6 +1306,8 @@ fn mainnet_genesis_config() -> GenesisConfiguration { initial_features_override: None, randomness_config_override: None, jwk_consensus_config_override: None, + initial_jwks: vec![], + keyless_groth16_vk_override: None, } } diff --git a/crates/aptos-genesis/src/builder.rs b/crates/aptos-genesis/src/builder.rs index 4117f9a4f2fe5..01921473e7c9b 100644 --- a/crates/aptos-genesis/src/builder.rs +++ b/crates/aptos-genesis/src/builder.rs @@ -27,6 +27,8 @@ use aptos_keygen::KeyGen; use aptos_logger::prelude::*; use aptos_types::{ chain_id::ChainId, + jwks::patch::IssuerJWK, + keyless::Groth16VerificationKey, on_chain_config::{ Features, GasScheduleV2, OnChainConsensusConfig, OnChainExecutionConfig, OnChainJWKConsensusConfig, OnChainRandomnessConfig, @@ -441,6 +443,8 @@ pub struct GenesisConfiguration { pub initial_features_override: Option, pub randomness_config_override: Option, pub jwk_consensus_config_override: Option, + pub initial_jwks: Vec, + pub keyless_groth16_vk_override: Option, } pub type InitConfigFn = Arc; @@ -662,6 +666,8 @@ impl Builder { initial_features_override: None, randomness_config_override: None, jwk_consensus_config_override: None, + initial_jwks: vec![], + keyless_groth16_vk_override: None, }; if let Some(init_genesis_config) = &self.init_genesis_config { (init_genesis_config)(&mut genesis_config); diff --git a/crates/aptos-genesis/src/config.rs b/crates/aptos-genesis/src/config.rs index 5e39ce08c17ac..bc6019f9cd661 100644 --- a/crates/aptos-genesis/src/config.rs +++ b/crates/aptos-genesis/src/config.rs @@ -6,6 +6,8 @@ use aptos_crypto::{bls12381, ed25519::Ed25519PublicKey, x25519}; use aptos_types::{ account_address::{AccountAddress, AccountAddressWithChecks}, chain_id::ChainId, + jwks::patch::IssuerJWK, + keyless::Groth16VerificationKey, network_address::{DnsName, NetworkAddress, Protocol}, on_chain_config::{OnChainConsensusConfig, OnChainExecutionConfig, OnChainJWKConsensusConfig}, transaction::authenticator::AuthenticationKey, @@ -75,7 +77,16 @@ pub struct Layout { pub on_chain_execution_config: OnChainExecutionConfig, /// An optional JWK consensus config to use, instead of `default_for_genesis()`. + #[serde(default)] pub jwk_consensus_config_override: Option, + + /// JWKs to patch in genesis. + #[serde(default)] + pub initial_jwks: Vec, + + /// Keyless Groth16 verification key to install in genesis. + #[serde(default)] + pub keyless_groth16_vk_override: Option, } impl Layout { @@ -116,6 +127,8 @@ impl Default for Layout { on_chain_consensus_config: OnChainConsensusConfig::default(), on_chain_execution_config: OnChainExecutionConfig::default_for_genesis(), jwk_consensus_config_override: None, + initial_jwks: vec![], + keyless_groth16_vk_override: None, } } } diff --git a/crates/aptos-genesis/src/lib.rs b/crates/aptos-genesis/src/lib.rs index 3a0cb84696545..78ac1940e4b69 100644 --- a/crates/aptos-genesis/src/lib.rs +++ b/crates/aptos-genesis/src/lib.rs @@ -23,6 +23,8 @@ use aptos_storage_interface::DbReaderWriter; use aptos_temppath::TempPath; use aptos_types::{ chain_id::ChainId, + jwks::patch::IssuerJWK, + keyless::Groth16VerificationKey, on_chain_config::{ Features, GasScheduleV2, OnChainConsensusConfig, OnChainExecutionConfig, OnChainJWKConsensusConfig, OnChainRandomnessConfig, @@ -76,6 +78,8 @@ pub struct GenesisInfo { pub initial_features_override: Option, pub randomness_config_override: Option, pub jwk_consensus_config_override: Option, + pub initial_jwks: Vec, + pub keyless_groth16_vk_override: Option, } impl GenesisInfo { @@ -115,6 +119,8 @@ impl GenesisInfo { initial_features_override: genesis_config.initial_features_override.clone(), randomness_config_override: genesis_config.randomness_config_override.clone(), jwk_consensus_config_override: genesis_config.jwk_consensus_config_override.clone(), + initial_jwks: genesis_config.initial_jwks.clone(), + keyless_groth16_vk_override: genesis_config.keyless_groth16_vk_override.clone(), }) } @@ -150,6 +156,8 @@ impl GenesisInfo { initial_features_override: self.initial_features_override.clone(), randomness_config_override: self.randomness_config_override.clone(), jwk_consensus_config_override: self.jwk_consensus_config_override.clone(), + initial_jwks: self.initial_jwks.clone(), + keyless_groth16_vk_override: self.keyless_groth16_vk_override.clone(), }, &self.consensus_config, &self.execution_config, diff --git a/crates/aptos-genesis/src/mainnet.rs b/crates/aptos-genesis/src/mainnet.rs index a3a1d27b6048d..d9a2246f1116d 100644 --- a/crates/aptos-genesis/src/mainnet.rs +++ b/crates/aptos-genesis/src/mainnet.rs @@ -143,6 +143,8 @@ impl MainnetGenesisInfo { initial_features_override: self.initial_features_override.clone(), randomness_config_override: self.randomness_config_override.clone(), jwk_consensus_config_override: self.jwk_consensus_config_override.clone(), + initial_jwks: vec![], + keyless_groth16_vk_override: None, }, ) } diff --git a/crates/aptos/src/genesis/mod.rs b/crates/aptos/src/genesis/mod.rs index 7dabbc9c609e5..001f9ae1a93ca 100644 --- a/crates/aptos/src/genesis/mod.rs +++ b/crates/aptos/src/genesis/mod.rs @@ -260,6 +260,8 @@ pub fn fetch_mainnet_genesis_info(git_options: GitOptions) -> CliTypedResult CliTypedResult Self { - Self::new(Self::SEED_ACCOUNTS_ROOT_SEED, 0) + pub fn new_for_seed_accounts(is_keyless: bool) -> Self { + Self::new(Self::SEED_ACCOUNTS_ROOT_SEED, 0, is_keyless) } - pub fn new_for_user_accounts(num_to_skip: u64) -> Self { - Self::new(Self::USER_ACCOUNTS_ROOT_SEED, num_to_skip) + pub fn new_for_user_accounts(num_to_skip: u64, is_keyless: bool) -> Self { + Self::new(Self::USER_ACCOUNTS_ROOT_SEED, num_to_skip, is_keyless) } - fn new(root_seed: u64, num_to_skip: u64) -> Self { + fn new(root_seed: u64, num_to_skip: u64, is_keyless: bool) -> Self { let mut root_rng = StdRng::seed_from_u64(root_seed); let num_rngs_to_skip = num_to_skip / Self::MAX_ACCOUNT_GEN_PER_RNG; for _ in 0..num_rngs_to_skip { @@ -35,14 +35,20 @@ impl AccountGenerator { let mut active_rng_quota = Self::MAX_ACCOUNT_GEN_PER_RNG - active_rng_to_skip; let mut active_rng = StdRng::seed_from_u64(root_rng.next_u64()); for _ in 0..active_rng_to_skip { - LocalAccount::generate(&mut active_rng); + LocalAccount::generate_for_testing(&mut active_rng, is_keyless); } let (sender, receiver) = mpsc::sync_channel(100 /* bound */); std::thread::Builder::new() .name("account_generator".to_string()) .spawn(move || { - while sender.send(LocalAccount::generate(&mut active_rng)).is_ok() { + while sender + .send(LocalAccount::generate_for_testing( + &mut active_rng, + is_keyless, + )) + .is_ok() + { active_rng_quota -= 1; if active_rng_quota == 0 { active_rng = StdRng::seed_from_u64(root_rng.next_u64()); diff --git a/execution/executor-benchmark/src/db_generator.rs b/execution/executor-benchmark/src/db_generator.rs index 23fb2375cc525..b11b66f4dbd99 100644 --- a/execution/executor-benchmark/src/db_generator.rs +++ b/execution/executor-benchmark/src/db_generator.rs @@ -16,9 +16,17 @@ use aptos_executor::{ db_bootstrapper::{generate_waypoint, maybe_bootstrap}, }; use aptos_storage_interface::DbReaderWriter; -use aptos_types::on_chain_config::Features; +use aptos_types::{ + jwks::{jwk::JWK, patch::IssuerJWK}, + keyless::{ + circuit_constants::TEST_GROTH16_KEYS, + test_utils::{get_sample_iss, get_sample_jwk}, + Groth16VerificationKey, + }, + on_chain_config::Features, +}; use aptos_vm::AptosVM; -use std::{fs, path::Path}; +use std::{fs, path::Path, sync::Arc}; pub fn create_db_with_accounts( num_accounts: usize, @@ -30,6 +38,7 @@ pub fn create_db_with_accounts( enable_storage_sharding: bool, pipeline_config: PipelineConfig, init_features: Features, + is_keyless: bool, ) where V: TransactionBlockExecutor + 'static, { @@ -59,6 +68,7 @@ pub fn create_db_with_accounts( enable_storage_sharding, pipeline_config, init_features, + is_keyless, ); } @@ -68,7 +78,15 @@ pub(crate) fn bootstrap_with_genesis( init_features: Features, ) { let (config, _genesis_key) = - aptos_genesis::test_utils::test_config_with_custom_features(init_features); + aptos_genesis::test_utils::test_config_with_custom_onchain(Some(Arc::new(move |config| { + config.initial_features_override = Some(init_features.clone()); + config.initial_jwks = vec![IssuerJWK { + issuer: get_sample_iss(), + jwk: JWK::RSA(get_sample_jwk()), + }]; + config.keyless_groth16_vk_override = + Some(Groth16VerificationKey::from(&TEST_GROTH16_KEYS.prepared_vk)); + }))); let mut rocksdb_configs = RocksdbConfigs::default(); rocksdb_configs.state_merkle_db_config.max_open_files = -1; diff --git a/execution/executor-benchmark/src/lib.rs b/execution/executor-benchmark/src/lib.rs index ff355f98a6c43..2ca47296bf9fe 100644 --- a/execution/executor-benchmark/src/lib.rs +++ b/execution/executor-benchmark/src/lib.rs @@ -119,6 +119,7 @@ pub fn run_benchmark( enable_storage_sharding: bool, pipeline_config: PipelineConfig, init_features: Features, + is_keyless: bool, ) where V: TransactionBlockExecutor + 'static, { @@ -163,6 +164,7 @@ pub fn run_benchmark( db.reader.clone(), num_accounts_to_be_loaded, num_accounts_to_skip, + is_keyless, ); let (main_signer_accounts, burner_accounts) = accounts_cache.split(num_main_signer_accounts); @@ -219,6 +221,7 @@ pub fn run_benchmark( source_dir, Some(num_accounts_to_load), pipeline_config.num_generator_workers, + is_keyless, ); let mut overall_measuring = OverallMeasuring::start(); @@ -340,6 +343,7 @@ pub fn add_accounts( enable_storage_sharding: bool, pipeline_config: PipelineConfig, init_features: Features, + is_keyless: bool, ) where V: TransactionBlockExecutor + 'static, { @@ -360,6 +364,7 @@ pub fn add_accounts( enable_storage_sharding, pipeline_config, init_features, + is_keyless, ); } @@ -374,6 +379,7 @@ fn add_accounts_impl( enable_storage_sharding: bool, pipeline_config: PipelineConfig, init_features: Features, + is_keyless: bool, ) where V: TransactionBlockExecutor + 'static, { @@ -400,6 +406,7 @@ fn add_accounts_impl( &source_dir, None, pipeline_config.num_generator_workers, + is_keyless, ); let start_time = Instant::now(); @@ -409,6 +416,7 @@ fn add_accounts_impl( num_new_accounts, init_account_balance, block_size, + is_keyless, ); generator.drop_sender(); pipeline.start_pipeline_processing(); @@ -905,6 +913,7 @@ mod tests { false, PipelineConfig::default(), features.clone(), + false, ); println!("run_benchmark"); @@ -935,6 +944,7 @@ mod tests { false, PipelineConfig::default(), features, + false, ); } diff --git a/execution/executor-benchmark/src/main.rs b/execution/executor-benchmark/src/main.rs index c3ec6c5fbb5c5..6454081ff8cc4 100644 --- a/execution/executor-benchmark/src/main.rs +++ b/execution/executor-benchmark/src/main.rs @@ -244,6 +244,9 @@ enum BlockExecutorTypeOpt { #[derive(Parser, Debug)] struct Opt { + #[clap(long)] + use_keyless_accounts: bool, + #[clap(long, default_value_t = 10000)] block_size: usize, @@ -443,6 +446,7 @@ where opt.enable_storage_sharding, opt.pipeline_opt.pipeline_config(), get_init_features(enable_feature, disable_feature), + opt.use_keyless_accounts, ); }, Command::RunExecutor { @@ -504,6 +508,7 @@ where opt.enable_storage_sharding, opt.pipeline_opt.pipeline_config(), get_init_features(enable_feature, disable_feature), + opt.use_keyless_accounts, ); }, Command::AddAccounts { @@ -523,6 +528,7 @@ where opt.enable_storage_sharding, opt.pipeline_opt.pipeline_config(), Features::default(), + opt.use_keyless_accounts, ); }, } diff --git a/execution/executor-benchmark/src/transaction_generator.rs b/execution/executor-benchmark/src/transaction_generator.rs index dde5b1a40454f..4703fa1a905b0 100644 --- a/execution/executor-benchmark/src/transaction_generator.rs +++ b/execution/executor-benchmark/src/transaction_generator.rs @@ -8,7 +8,10 @@ use crate::{ }; use aptos_crypto::ed25519::Ed25519PrivateKey; use aptos_logger::info; -use aptos_sdk::{transaction_builder::TransactionFactory, types::LocalAccount}; +use aptos_sdk::{ + transaction_builder::{aptos_stdlib, TransactionFactory}, + types::LocalAccount, +}; use aptos_storage_interface::{state_view::LatestDbStateCheckpointView, DbReader, DbReaderWriter}; use aptos_types::{ account_address::AccountAddress, @@ -154,11 +157,12 @@ impl TransactionGenerator { reader: Arc, num_accounts: usize, num_to_skip: usize, + is_keyless: bool, ) -> AccountCache { Self::resync_sequence_numbers( reader, Self::gen_account_cache( - AccountGenerator::new_for_user_accounts(num_to_skip as u64), + AccountGenerator::new_for_user_accounts(num_to_skip as u64, is_keyless), num_accounts, "user", ), @@ -166,11 +170,15 @@ impl TransactionGenerator { ) } - fn gen_seed_account_cache(reader: Arc, num_accounts: usize) -> AccountCache { + fn gen_seed_account_cache( + reader: Arc, + num_accounts: usize, + is_keyless: bool, + ) -> AccountCache { Self::resync_sequence_numbers( reader, Self::gen_account_cache( - AccountGenerator::new_for_seed_accounts(), + AccountGenerator::new_for_seed_accounts(is_keyless), num_accounts, "seed", ), @@ -185,6 +193,7 @@ impl TransactionGenerator { db_dir: P, num_main_signer_accounts: Option, num_workers: usize, + is_keyless: bool, ) -> Self { let num_existing_accounts = TransactionGenerator::read_meta(&db_dir); @@ -194,7 +203,7 @@ impl TransactionGenerator { main_signer_accounts: num_main_signer_accounts.map(|num_main_signer_accounts| { let num_cached_accounts = std::cmp::min(num_existing_accounts, num_main_signer_accounts); - Self::gen_user_account_cache(db.reader.clone(), num_cached_accounts, 0) + Self::gen_user_account_cache(db.reader.clone(), num_cached_accounts, 0, is_keyless) }), num_existing_accounts, block_sender: Some(block_sender), @@ -256,6 +265,7 @@ impl TransactionGenerator { num_new_accounts: usize, init_account_balance: u64, block_size: usize, + is_keyless: bool, ) { assert!(self.block_sender.is_some()); // Ensure that seed accounts have enough balance to transfer money to at least 10000 account with @@ -265,12 +275,14 @@ impl TransactionGenerator { num_new_accounts, block_size, init_account_balance * 10_000, + is_keyless, ); self.create_and_fund_accounts( num_existing_accounts, num_new_accounts, init_account_balance, block_size, + is_keyless, ); } @@ -351,11 +363,13 @@ impl TransactionGenerator { num_new_accounts: usize, block_size: usize, seed_account_balance: u64, + is_keyless: bool, ) { // We don't store the # of existing seed accounts now. Thus here we just blindly re-create // and re-mint seed accounts here. let num_seed_accounts = (num_new_accounts / 1000).clamp(1, 100000); - let seed_accounts_cache = Self::gen_seed_account_cache(reader, num_seed_accounts); + let seed_accounts_cache = + Self::gen_seed_account_cache(reader, num_seed_accounts, is_keyless); println!( "[{}] Generating {} seed account creation txns, with {} coins.", @@ -374,13 +388,12 @@ impl TransactionGenerator { let transactions: Vec<_> = chunk .iter() .map(|new_account| { - let txn = self.root_account.sign_with_transaction_builder( - self.transaction_factory - .implicitly_create_user_account_and_transfer( - new_account.public_key(), - seed_account_balance, - ), + let payload = aptos_stdlib::aptos_account_transfer( + new_account.authentication_key().account_address(), + seed_account_balance, ); + let builder = self.transaction_factory.payload(payload); + let txn = self.root_account.sign_with_transaction_builder(builder); Transaction::UserTransaction(txn) }) .collect(); @@ -401,13 +414,15 @@ impl TransactionGenerator { num_new_accounts: usize, init_account_balance: u64, block_size: usize, + is_keyless: bool, ) { println!( "[{}] Generating {} account creation txns.", now_fmt!(), num_new_accounts ); - let mut generator = AccountGenerator::new_for_user_accounts(num_existing_accounts as u64); + let mut generator = + AccountGenerator::new_for_user_accounts(num_existing_accounts as u64, is_keyless); println!("Skipped first {} existing accounts.", num_existing_accounts); let bar = get_progress_bar(num_new_accounts); @@ -431,13 +446,12 @@ impl TransactionGenerator { Arc::new(AtomicUsize::new(0)), |(sender_idx, new_account), account_cache| { let sender = &account_cache.accounts[sender_idx]; - let txn = sender.sign_with_transaction_builder( - self.transaction_factory - .implicitly_create_user_account_and_transfer( - new_account.public_key(), - init_account_balance, - ), + let payload = aptos_stdlib::aptos_account_transfer( + new_account.authentication_key().account_address(), + init_account_balance, ); + let txn = sender + .sign_with_transaction_builder(self.transaction_factory.payload(payload)); Some(Transaction::UserTransaction(txn)) }, |(sender_idx, _)| *sender_idx, diff --git a/sdk/Cargo.toml b/sdk/Cargo.toml index 26e12125ab42b..2bcd8106bca34 100644 --- a/sdk/Cargo.toml +++ b/sdk/Cargo.toml @@ -25,6 +25,7 @@ bcs = { workspace = true } ed25519-dalek-bip32 = { workspace = true } hex = { workspace = true } move-core-types = { workspace = true } +rand = { workspace = true } rand_core = { workspace = true } reqwest = { workspace = true } serde = { workspace = true } diff --git a/sdk/src/types.rs b/sdk/src/types.rs index 941cdf0a4c55e..1e849aedf9d60 100644 --- a/sdk/src/types.rs +++ b/sdk/src/types.rs @@ -24,13 +24,14 @@ use aptos_types::{ event::EventKey, keyless::{ Claims, Configuration, EphemeralCertificate, IdCommitment, KeylessPublicKey, - KeylessSignature, OpenIdSig, Pepper, TransactionAndProof, ZeroKnowledgeSig, + KeylessSignature, OpenIdSig, Pepper, ZeroKnowledgeSig, }, transaction::authenticator::{AnyPublicKey, EphemeralPublicKey, EphemeralSignature}, }; use bip39::{Language, Mnemonic, Seed}; use ed25519_dalek_bip32::{DerivationPath, ExtendedSecretKey}; use keyless::FederatedKeylessPublicKey; +use rand::Rng; use serde::{Deserialize, Serialize}; use std::{ str::FromStr, @@ -73,7 +74,7 @@ impl LocalAccountAuthenticator { account: &impl CommonKeylessAccount, ) -> KeylessSignature { let proof = account.zk_sig().proof; - let txn_and_zkp = TransactionAndProof { + let txn_and_zkp = keyless::TransactionAndProof { message: txn, proof: Some(proof), }; @@ -182,6 +183,91 @@ impl LocalAccount { Ok(Self::new(address, key, sequence_number)) } + pub fn generate_for_testing(rng: &mut R1, keyless_mode: bool) -> Self + where + R1: Rng + rand_core::CryptoRng, + { + if keyless_mode { + let config = keyless::Configuration::new_for_testing(); + let now_secs = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .unwrap() + .as_secs(); + let esk = EphemeralPrivateKey::Ed25519 { + inner_private_key: Ed25519PrivateKey::generate(rng), + }; + let exp_timestamp_secs = now_secs + 7 * 86400; // + 7 days + let exp_horizon_secs = 100 * 86400; // 100 days + let blinder = vec![0x01; 31]; + let eph_key_pair = + EphemeralKeyPair::new(&config, esk, exp_timestamp_secs, blinder).unwrap(); + + // Simulation of OIDC provider processing. + let iss = keyless::test_utils::get_sample_iss(); + let jwk = keyless::test_utils::get_sample_jwk(); + let aud = format!("aud_{}", hex::encode(rng.gen::<[u8; 4]>())); + let uid_key = "sub".to_string(); + let uid_val = format!("uid_{}", hex::encode(rng.gen::<[u8; 4]>())); + let jwt_header = keyless::test_utils::get_sample_jwt_header_json(); + let jwt_header_b64 = keyless::base64url_encode_str(&jwt_header); + let jwt_payload = keyless::circuit_testcases::render_jwt_payload_json( + &iss, + &aud, + &uid_key, + &uid_val, + "", + now_secs, + &eph_key_pair.nonce, + now_secs + 86400, + ); + let jwt_payload_b64 = keyless::base64url_encode_str(&jwt_payload); + let jwt_msg = format!("{}.{}", jwt_header_b64, jwt_payload_b64); + let jwt_sig = keyless::test_utils::oidc_provider_sign( + *keyless::circuit_testcases::SAMPLE_JWK_SK, + jwt_msg.as_bytes(), + ); + let jwt_sig_b64 = base64::encode_config(jwt_sig, base64::URL_SAFE_NO_PAD); + let jwt = format!("{}.{}", jwt_msg, jwt_sig_b64); + + let pepper = keyless::test_utils::get_sample_pepper(); + let idc = keyless::IdCommitment::new_from_preimage(&pepper, &aud, &uid_key, &uid_val) + .unwrap(); + let public_inputs = keyless::bn254_circom::hash_public_inputs( + &eph_key_pair.public_key, + &idc, + exp_timestamp_secs, + exp_horizon_secs, + &iss, + None, + &jwt_header, + &jwk, + None, + &config, + ) + .unwrap(); + let groth16_proof = keyless::proof_simulation::Groth16SimulatorBn254::create_random_proof_with_trapdoor(&[public_inputs], &keyless::circuit_constants::TEST_GROTH16_KEYS.pk, rng).unwrap(); + let zk_sig = ZeroKnowledgeSig { + proof: keyless::ZKP::Groth16(groth16_proof), + exp_horizon_secs, + extra_field: None, + override_aud_val: None, + training_wheels_signature: None, + }; + // zk_sig.verify_groth16_proof(public_inputs, &TEST_GROTH16_KEYS.prepared_vk).unwrap(); + let keyless_account = + KeylessAccount::new_from_jwt(&jwt, eph_key_pair, Some(&uid_key), pepper, zk_sig) + .unwrap(); + + Self::new_keyless( + keyless_account.authentication_key().account_address(), + keyless_account, + 0, + ) + } else { + Self::generate(rng) + } + } + /// Generate a new account locally. Note: This function does not actually /// create an account on the Aptos blockchain, it just generates a new /// account locally. @@ -468,7 +554,7 @@ pub struct AccountKey { impl AccountKey { pub fn generate(rng: &mut R) -> Self where - R: ::rand_core::RngCore + ::rand_core::CryptoRng, + R: rand_core::RngCore + rand_core::CryptoRng, { let private_key = Ed25519PrivateKey::generate(rng); Self::from_private_key(private_key) @@ -555,7 +641,6 @@ impl EphemeralPrivateKey { pub struct EphemeralKeyPair { private_key: EphemeralPrivateKey, public_key: EphemeralPublicKey, - #[allow(dead_code)] nonce: String, expiry_date_secs: u64, blinder: Vec, @@ -563,17 +648,13 @@ pub struct EphemeralKeyPair { impl EphemeralKeyPair { pub fn new( + config: &Configuration, private_key: EphemeralPrivateKey, expiry_date_secs: u64, blinder: Vec, ) -> Result { let epk = private_key.public_key(); - let nonce = OpenIdSig::reconstruct_oauth_nonce( - &blinder, - expiry_date_secs, - &epk, - &Configuration::new_for_devnet(), - )?; + let nonce = OpenIdSig::reconstruct_oauth_nonce(&blinder, expiry_date_secs, &epk, config)?; Ok(Self { private_key, @@ -585,6 +666,7 @@ impl EphemeralKeyPair { } pub fn new_ed25519( + config: &keyless::Configuration, private_key: Ed25519PrivateKey, expiry_date_secs: u64, blinder: Vec, @@ -592,7 +674,7 @@ impl EphemeralKeyPair { let esk = EphemeralPrivateKey::Ed25519 { inner_private_key: private_key, }; - Self::new(esk, expiry_date_secs, blinder) + Self::new(config, esk, expiry_date_secs, blinder) } } @@ -984,6 +1066,7 @@ mod tests { #[ignore] #[tokio::test] async fn test_derive_keyless_account() { + let config = Configuration::new_for_testing(); let aptos_rest_client = Client::builder(AptosBaseUrl::Devnet).build(); // This JWT is taken from https://github.com/aptos-labs/aptos-ts-sdk/blob/f644e61beb70e69dfd489e75287c67b527385135/tests/e2e/api/keyless.test.ts#L11 // As is the ephemeralKeyPair @@ -994,7 +1077,7 @@ mod tests { .unwrap(); let esk = Ed25519PrivateKey::try_from(sk_bytes.as_slice()).unwrap(); let ephemeral_key_pair = - EphemeralKeyPair::new_ed25519(esk, 1735475012, vec![0; 31]).unwrap(); + EphemeralKeyPair::new_ed25519(&config, esk, 1735475012, vec![0; 31]).unwrap(); let mut account = derive_keyless_account(&aptos_rest_client, jwt, ephemeral_key_pair, None) .await .unwrap(); diff --git a/testsuite/smoke-test/src/keyless.rs b/testsuite/smoke-test/src/keyless.rs index f128c6aef9eca..7c3e61610c78f 100644 --- a/testsuite/smoke-test/src/keyless.rs +++ b/testsuite/smoke-test/src/keyless.rs @@ -1,7 +1,7 @@ // Copyright © Aptos Foundation // SPDX-License-Identifier: Apache-2.0 -use crate::smoke_test_environment::SwarmBuilder; +use crate::{smoke_test_environment::SwarmBuilder, utils::get_on_chain_resource}; use aptos::{common::types::GasOptions, test::CliTestFramework}; use aptos_cached_packages::aptos_stdlib; use aptos_crypto::{ @@ -324,8 +324,15 @@ script {{ let esk = EphemeralPrivateKey::Ed25519 { inner_private_key: get_sample_esk(), }; - let ephemeral_key_pair = - EphemeralKeyPair::new(esk, get_sample_exp_date(), get_sample_epk_blinder()).unwrap(); + let rest_cli = swarm.validators().next().unwrap().rest_client(); + let config = get_on_chain_resource(&rest_cli).await; + let ephemeral_key_pair = EphemeralKeyPair::new( + &config, + esk, + get_sample_exp_date(), + get_sample_epk_blinder(), + ) + .unwrap(); let federated_keyless_account = FederatedKeylessAccount::new_from_jwt( &get_sample_jwt_token(), ephemeral_key_pair, @@ -430,11 +437,19 @@ async fn test_keyless_no_training_wheels_groth16_verifies() { async fn test_keyless_groth16_verifies_using_rust_sdk() { let (_tw_sk, _, _, swarm, mut cli, root_idx) = setup_local_net().await; + let rest_cli = swarm.validators().next().unwrap().rest_client(); + let config = get_on_chain_resource(&rest_cli).await; + let esk = EphemeralPrivateKey::Ed25519 { inner_private_key: get_sample_esk(), }; - let ephemeral_key_pair = - EphemeralKeyPair::new(esk, get_sample_exp_date(), get_sample_epk_blinder()).unwrap(); + let ephemeral_key_pair = EphemeralKeyPair::new( + &config, + esk, + get_sample_exp_date(), + get_sample_epk_blinder(), + ) + .unwrap(); let mut info = swarm.aptos_public_info(); let keyless_account = KeylessAccount::new( @@ -489,12 +504,20 @@ async fn test_keyless_groth16_verifies_using_rust_sdk() { #[tokio::test] async fn test_keyless_groth16_verifies_using_rust_sdk_from_jwt() { let (_tw_sk, _, _, swarm, mut cli, root_idx) = setup_local_net().await; + let rest_cli = swarm.validators().next().unwrap().rest_client(); + let config = get_on_chain_resource(&rest_cli).await; let esk = EphemeralPrivateKey::Ed25519 { inner_private_key: get_sample_esk(), }; - let ephemeral_key_pair = - EphemeralKeyPair::new(esk, get_sample_exp_date(), get_sample_epk_blinder()).unwrap(); + + let ephemeral_key_pair = EphemeralKeyPair::new( + &config, + esk, + get_sample_exp_date(), + get_sample_epk_blinder(), + ) + .unwrap(); let mut info = swarm.aptos_public_info(); let keyless_account = KeylessAccount::new_from_jwt( diff --git a/types/src/jwks/patch/mod.rs b/types/src/jwks/patch/mod.rs index cdcc693551ba5..0dce3842ce8fb 100644 --- a/types/src/jwks/patch/mod.rs +++ b/types/src/jwks/patch/mod.rs @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 use crate::{ - jwks::jwk::JWKMoveStruct, + jwks::jwk::{JWKMoveStruct, JWK}, move_any::{Any as MoveAny, AsMoveAny}, move_utils::as_move_value::AsMoveValue, }; @@ -36,3 +36,10 @@ pub struct PatchUpsertJWK { impl AsMoveAny for PatchUpsertJWK { const MOVE_TYPE_NAME: &'static str = "0x1::jwks::PatchUpsertJWK"; } + +/// A variant representation used in genesis layout. +#[derive(Clone, Debug, Deserialize, Serialize)] +pub struct IssuerJWK { + pub issuer: String, + pub jwk: JWK, +} diff --git a/types/src/jwks/rsa/insecure_test_jwk.json b/types/src/jwks/rsa/insecure_test_jwk.json index 0e6524dccd8d9..2e109850809b8 100644 --- a/types/src/jwks/rsa/insecure_test_jwk.json +++ b/types/src/jwks/rsa/insecure_test_jwk.json @@ -4,7 +4,7 @@ "kty": "RSA", "n": "6S7asUuzq5Q_3U9rbs-PkDVIdjgmtgWreG5qWPsC9xXZKiMV1AiV9LXyqQsAYpCqEDM3XbfmZqGb48yLhb_XqZaKgSYaC_h2DjM7lgrIQAp9902Rr8fUmLN2ivr5tnLxUUOnMOc2SQtr9dgzTONYW5Zu3PwyvAWk5D6ueIUhLtYzpcB-etoNdL3Ir2746KIy_VUsDwAM7dhrqSK8U2xFCGlau4ikOTtvzDownAMHMrfE7q1B6WZQDAQlBmxRQsyKln5DIsKv6xauNsHRgBAKctUxZG8M4QJIx3S6Aughd3RZC4Ca5Ae9fd8L8mlNYBCrQhOZ7dS0f4at4arlLcajtw", "e": "AQAB", - "kid": "test-rsa", + "kid": "test-rsa-insecure", "alg": "RS256" } ] diff --git a/types/src/keyless/bn254_circom.rs b/types/src/keyless/bn254_circom.rs index 1c37c1d8b176b..9815c00100371 100644 --- a/types/src/keyless/bn254_circom.rs +++ b/types/src/keyless/bn254_circom.rs @@ -9,6 +9,7 @@ use crate::{ KeylessSignature, }, serialize, + transaction::authenticator::EphemeralPublicKey, }; use anyhow::bail; use aptos_crypto::{poseidon_bn254, poseidon_bn254::pad_and_hash_string, CryptoMaterialError}; @@ -252,7 +253,7 @@ static PAD_AND_HASH_STRING_CACHE: Lazy> = static JWK_HASH_CACHE: Lazy> = Lazy::new(|| Cache::new(100)); -pub fn cached_pad_and_hash_string(str: &String, max_bytes: usize) -> anyhow::Result { +pub fn cached_pad_and_hash_string(str: &str, max_bytes: usize) -> anyhow::Result { let key = (str.to_string(), max_bytes); match PAD_AND_HASH_STRING_CACHE.get(&key) { None => { @@ -275,6 +276,93 @@ pub fn cached_jwk_hash(jwk: &RSA_JWK) -> anyhow::Result { } } +pub fn hash_public_inputs( + epk: &EphemeralPublicKey, + idc: &IdCommitment, + exp_timestamp_secs: u64, + exp_horizon_secs: u64, + iss: &str, + extra_field: Option, + jwt_header: &str, + jwk: &RSA_JWK, + override_aud_val: Option, + config: &Configuration, +) -> anyhow::Result { + let mut epk_frs = poseidon_bn254::keyless::pad_and_pack_bytes_to_scalars_with_len( + epk.to_bytes().as_slice(), + config.max_commited_epk_bytes as usize, + )?; + + let idc = Fr::from_le_bytes_mod_order(&idc.0); + + let exp_timestamp_secs = Fr::from(exp_timestamp_secs); + + let exp_horizon_secs = Fr::from(exp_horizon_secs); + + let iss_field_hash = cached_pad_and_hash_string(iss, config.max_iss_val_bytes as usize)?; + + let (has_extra_field, extra_field_hash) = match &extra_field { + None => (Fr::zero(), *EMPTY_EXTRA_FIELD_HASH), + Some(extra_field) => ( + Fr::one(), + poseidon_bn254::keyless::pad_and_hash_string( + extra_field, + config.max_extra_field_bytes as usize, + )?, + ), + }; + + let jwt_header_b64_with_separator = format!("{}.", base64url_encode_str(jwt_header)); + let jwt_header_hash = cached_pad_and_hash_string( + &jwt_header_b64_with_separator, + config.max_jwt_header_b64_bytes as usize, + )?; + + let jwk_hash = cached_jwk_hash(jwk)?; + + let (override_aud_val_hash, use_override_aud) = match &override_aud_val { + Some(override_aud_val) => ( + cached_pad_and_hash_string(override_aud_val, IdCommitment::MAX_AUD_VAL_BYTES)?, + ark_bn254::Fr::from(1), + ), + None => (*EMPTY_OVERRIDE_AUD_FIELD_HASH, ark_bn254::Fr::from(0)), + }; + + // println!("Num EPK scalars: {}", epk_frs.len()); + // for (i, e) in epk_frs.iter().enumerate() { + // println!("EPK Fr[{}]: {}", i, e.to_string()) + // } + // println!("IDC: {}", idc); + // println!("exp_timestamp_secs: {}", exp_timestamp_secs); + // println!("exp_horizon_secs: {}", exp_horizon_secs); + // println!("iss field: {}", pk.iss_val); + // println!("iss field hash: {}", iss_field_hash); + // println!("Has extra field: {}", has_extra_field); + // println!("Extra field val: {:?}", proof.extra_field); + // println!("Extra field hash: {}", extra_field_hash); + // println!("JWT header val: {}", jwt_header_b64_with_separator); + // println!("JWT header hash: {}", jwt_header_hash); + // println!("JWK hash: {}", jwk_hash); + // println!("Override aud hash: {}", override_aud_val_hash); + // println!("Use override aud: {}", use_override_aud.to_string()); + + let mut frs = vec![]; + frs.append(&mut epk_frs); + frs.push(idc); + frs.push(exp_timestamp_secs); + frs.push(exp_horizon_secs); + frs.push(iss_field_hash); + frs.push(has_extra_field); + frs.push(extra_field_hash); + frs.push(jwt_header_hash); + frs.push(jwk_hash); + frs.push(override_aud_val_hash); + frs.push(use_override_aud); + // TODO(keyless): If we plan on avoiding verifying the same PIH twice, there should be no + // need for caching here. If we do not, we should cache the result here too. + poseidon_bn254::hash_scalars(frs) +} + pub fn get_public_inputs_hash( sig: &KeylessSignature, pk: &KeylessPublicKey, @@ -282,87 +370,18 @@ pub fn get_public_inputs_hash( config: &Configuration, ) -> anyhow::Result { if let EphemeralCertificate::ZeroKnowledgeSig(proof) = &sig.cert { - let (has_extra_field, extra_field_hash) = match &proof.extra_field { - None => (Fr::zero(), *EMPTY_EXTRA_FIELD_HASH), - Some(extra_field) => ( - Fr::one(), - poseidon_bn254::keyless::pad_and_hash_string( - extra_field, - config.max_extra_field_bytes as usize, - )?, - ), - }; - - let (override_aud_val_hash, use_override_aud) = match &proof.override_aud_val { - Some(override_aud_val) => ( - cached_pad_and_hash_string(override_aud_val, IdCommitment::MAX_AUD_VAL_BYTES)?, - ark_bn254::Fr::from(1), - ), - None => (*EMPTY_OVERRIDE_AUD_FIELD_HASH, ark_bn254::Fr::from(0)), - }; - - // Add the hash of the jwt_header with the "." separator appended - let jwt_header_b64_with_separator = - format!("{}.", base64url_encode_str(sig.jwt_header_json.as_str())); - let jwt_header_hash = cached_pad_and_hash_string( - &jwt_header_b64_with_separator, - config.max_jwt_header_b64_bytes as usize, - )?; - - let jwk_hash = cached_jwk_hash(jwk)?; - - // Add the hash of the value of the `iss` field - let iss_field_hash = - cached_pad_and_hash_string(&pk.iss_val, config.max_iss_val_bytes as usize)?; - - // Add the id_commitment as a scalar - let idc = Fr::from_le_bytes_mod_order(&pk.idc.0); - - // Add the exp_timestamp_secs as a scalar - let exp_timestamp_secs = Fr::from(sig.exp_date_secs); - - // Add the epk lifespan as a scalar - let exp_horizon_secs = Fr::from(proof.exp_horizon_secs); - - // Add the epk as padded and packed scalars - let mut epk_frs = poseidon_bn254::keyless::pad_and_pack_bytes_to_scalars_with_len( - sig.ephemeral_pubkey.to_bytes().as_slice(), - config.max_commited_epk_bytes as usize, - )?; - - // println!("Num EPK scalars: {}", epk_frs.len()); - // for (i, e) in epk_frs.iter().enumerate() { - // println!("EPK Fr[{}]: {}", i, e.to_string()) - // } - // println!("IDC: {}", idc); - // println!("exp_timestamp_secs: {}", exp_timestamp_secs); - // println!("exp_horizon_secs: {}", exp_horizon_secs); - // println!("iss field: {}", pk.iss_val); - // println!("iss field hash: {}", iss_field_hash); - // println!("Has extra field: {}", has_extra_field); - // println!("Extra field val: {:?}", proof.extra_field); - // println!("Extra field hash: {}", extra_field_hash); - // println!("JWT header val: {}", jwt_header_b64_with_separator); - // println!("JWT header hash: {}", jwt_header_hash); - // println!("JWK hash: {}", jwk_hash); - // println!("Override aud hash: {}", override_aud_val_hash); - // println!("Use override aud: {}", use_override_aud.to_string()); - - let mut frs = vec![]; - frs.append(&mut epk_frs); - frs.push(idc); - frs.push(exp_timestamp_secs); - frs.push(exp_horizon_secs); - frs.push(iss_field_hash); - frs.push(has_extra_field); - frs.push(extra_field_hash); - frs.push(jwt_header_hash); - frs.push(jwk_hash); - frs.push(override_aud_val_hash); - frs.push(use_override_aud); - // TODO(keyless): If we plan on avoiding verifying the same PIH twice, there should be no - // need for caching here. If we do not, we should cache the result here too. - poseidon_bn254::hash_scalars(frs) + hash_public_inputs( + &sig.ephemeral_pubkey, + &pk.idc, + sig.exp_date_secs, + proof.exp_horizon_secs, + &pk.iss_val, + proof.extra_field.clone(), + &sig.jwt_header_json, + jwk, + proof.override_aud_val.clone(), + config, + ) } else { bail!("Can only call `get_public_inputs_hash` on keyless::Signature with Groth16 ZK proof") } diff --git a/types/src/keyless/circuit_constants.rs b/types/src/keyless/circuit_constants.rs index 60cf6a96f914e..1cd02f384b140 100644 --- a/types/src/keyless/circuit_constants.rs +++ b/types/src/keyless/circuit_constants.rs @@ -3,10 +3,15 @@ //! These constants are from commit 125522b4b226f8ece3e3162cecfefe915d13bc30 of keyless-circuit. -use crate::keyless::bn254_circom::{g1_projective_str_to_affine, g2_projective_str_to_affine}; +use crate::keyless::{ + bn254_circom::{g1_projective_str_to_affine, g2_projective_str_to_affine}, + proof_simulation::{Groth16SimulatorBn254, Trapdoor}, +}; use aptos_crypto::poseidon_bn254; use ark_bn254::Bn254; use ark_groth16::{PreparedVerifyingKey, VerifyingKey}; +use once_cell::sync::Lazy; +use rand::{prelude::StdRng, SeedableRng}; pub(crate) const MAX_AUD_VAL_BYTES: usize = 120; pub(crate) const MAX_UID_KEY_BYTES: usize = 30; @@ -92,3 +97,21 @@ pub fn devnet_prepared_vk() -> PreparedVerifyingKey { PreparedVerifyingKey::from(vk) } + +pub struct Groth16Keys { + pub pk: Trapdoor, + pub vk: VerifyingKey, + pub prepared_vk: PreparedVerifyingKey, +} + +pub static TEST_GROTH16_KEYS: Lazy = Lazy::new(|| { + let mut rng = StdRng::seed_from_u64(999); + let (pk, vk) = + Groth16SimulatorBn254::circuit_agnostic_setup_with_trapdoor(&mut rng, 1).unwrap(); + let prepared_vk = PreparedVerifyingKey::from(vk.clone()); + Groth16Keys { + pk, + vk, + prepared_vk, + } +}); diff --git a/types/src/keyless/circuit_testcases.rs b/types/src/keyless/circuit_testcases.rs index b542d26965232..bb6896bb45bc0 100644 --- a/types/src/keyless/circuit_testcases.rs +++ b/types/src/keyless/circuit_testcases.rs @@ -61,6 +61,31 @@ pub fn sample_jwt_payload_json() -> String { ) } +pub fn render_jwt_payload_json( + iss: &str, + aud: &str, + uid_key: &str, + uid_val: &str, + extra_field: &str, + iat: u64, + nonce: &str, + exp: u64, +) -> String { + format!( + r#"{{ + "iss":"{}", + "aud":"{}", + "{}":"{}", + {} + "iat":{}, + "nonce":"{}", + "exp":{} + }} + "#, + iss, aud, uid_key, uid_val, extra_field, iat, nonce, exp + ) +} + pub fn sample_jwt_payload_json_overrides( iss: &str, uid_val: &str, @@ -109,7 +134,7 @@ pub(crate) static SAMPLE_JWK: Lazy = Lazy::new(insecure_test_rsa_jwk); /// This is the SK from https://token.dev/. /// To convert it into a JSON, you can use https://irrte.ch/jwt-js-decode/pem2jwk.html -pub(crate) static SAMPLE_JWK_SK: Lazy<&RsaKeyPair> = Lazy::new(|| &*INSECURE_TEST_RSA_KEY_PAIR); +pub static SAMPLE_JWK_SK: Lazy<&RsaKeyPair> = Lazy::new(|| &*INSECURE_TEST_RSA_KEY_PAIR); pub(crate) const SAMPLE_UID_KEY: &str = "sub"; diff --git a/types/src/keyless/configuration.rs b/types/src/keyless/configuration.rs index b5b444110670a..58818d1a3063e 100644 --- a/types/src/keyless/configuration.rs +++ b/types/src/keyless/configuration.rs @@ -7,6 +7,7 @@ use crate::{ circuit_constants, circuit_testcases::SAMPLE_EXP_HORIZON_SECS, KEYLESS_ACCOUNT_MODULE_NAME, }, move_utils::as_move_value::AsMoveValue, + on_chain_config::OnChainConfig, }; use move_core_types::{ ident_str, @@ -92,3 +93,8 @@ impl Configuration { } } } + +impl OnChainConfig for Configuration { + const MODULE_IDENTIFIER: &'static str = KEYLESS_ACCOUNT_MODULE_NAME; + const TYPE_IDENTIFIER: &'static str = "Configuration"; +} diff --git a/types/src/keyless/mod.rs b/types/src/keyless/mod.rs index 1e92aa1abf629..39b546673a723 100644 --- a/types/src/keyless/mod.rs +++ b/types/src/keyless/mod.rs @@ -21,8 +21,8 @@ use std::{ time::{Duration, SystemTime, UNIX_EPOCH}, }; -mod bn254_circom; -mod circuit_constants; +pub mod bn254_circom; +pub mod circuit_constants; pub mod circuit_testcases; mod configuration; mod groth16_sig; @@ -420,7 +420,7 @@ pub fn get_authenticators( Ok(authenticators) } -pub(crate) fn base64url_encode_str(data: &str) -> String { +pub fn base64url_encode_str(data: &str) -> String { base64::encode_config(data.as_bytes(), URL_SAFE_NO_PAD) } diff --git a/types/src/keyless/test_utils.rs b/types/src/keyless/test_utils.rs index c8babbd5913af..1d275ff3c8b30 100644 --- a/types/src/keyless/test_utils.rs +++ b/types/src/keyless/test_utils.rs @@ -29,7 +29,7 @@ use ark_groth16::{prepare_verifying_key, PreparedVerifyingKey}; use base64::{encode_config, URL_SAFE_NO_PAD}; use move_core_types::account_address::AccountAddress; use once_cell::sync::Lazy; -use ring::signature; +use ring::{signature, signature::RsaKeyPair}; static DUMMY_EPHEMERAL_SIGNATURE: Lazy = Lazy::new(|| { let sk = Ed25519PrivateKey::generate_for_testing(); @@ -308,6 +308,20 @@ pub fn get_sample_jwt_token_from_payload(payload: &str) -> String { format!("{}.{}", msg, base64url_string) } +pub fn oidc_provider_sign(sk: &RsaKeyPair, msg: &[u8]) -> Vec { + let mut jwt_sig = vec![0u8; sk.public_modulus_len()]; + let rng = ring::rand::SystemRandom::new(); + sk.sign( + &signature::RSA_PKCS1_SHA256, + &rng, + msg, + jwt_sig.as_mut_slice(), + ) + .unwrap(); + + jwt_sig +} + /// Note: Does not have a valid ephemeral signature. Use the SAMPLE_ESK to compute one over the /// desired TXN. pub fn get_sample_openid_sig_and_pk() -> (KeylessSignature, KeylessPublicKey) {