diff --git a/aptos-move/framework/aptos-framework/doc/account.md b/aptos-move/framework/aptos-framework/doc/account.md index 9565d16e617ad..d17c58a984c9c 100644 --- a/aptos-move/framework/aptos-framework/doc/account.md +++ b/aptos-move/framework/aptos-framework/doc/account.md @@ -1143,8 +1143,11 @@ is returned. This way, the caller of this function can publish additional resour ## Function `rotate_authentication_key_internal` -This function is used to rotate a resource account's authentication key to 0, so that no private key can control -the resource account. +This function is used to rotate a resource account's authentication key to new_auth_key. This is done in +many contexts: +1. During normal key rotation via rotate_authentication_key or rotate_authentication_key_call +2. During resource account initialization so that no private key can control the resource account +3. During multisig_v2 account creation
public(friend) fun rotate_authentication_key_internal(account: &signer, new_auth_key: vector<u8>)
@@ -1176,7 +1179,11 @@ the resource account.
 
 ## Function `rotate_authentication_key_call`
 
-Entry function-only rotation key function that allows the signer update their authentication_key.
+Private entry function for key rotation that allows the signer to update their authentication key.
+Note that this does not update the OriginatingAddress table because the new_auth_key is not "verified": it
+does not come with a proof-of-knowledge of the underlying SK. Nonetheless, we need this functionality due to
+the introduction of non-standard key algorithms, such as passkeys, which cannot produce proofs-of-knowledge in
+the format expected in rotate_authentication_key.
 
 
 
entry fun rotate_authentication_key_call(account: &signer, new_auth_key: vector<u8>)
@@ -1188,15 +1195,8 @@ Entry function-only rotation key function that allows the signer update their au
 Implementation
 
 
-
entry fun rotate_authentication_key_call(account: &signer, new_auth_key: vector<u8>) acquires Account, OriginatingAddress {
-    let addr = signer::address_of(account);
-    assert!(exists_at(addr), error::not_found(EACCOUNT_DOES_NOT_EXIST));
-    assert!(
-        vector::length(&new_auth_key) == 32,
-        error::invalid_argument(EMALFORMED_AUTHENTICATION_KEY)
-    );
-    let account_resource = borrow_global_mut<Account>(addr);
-    update_auth_key_and_originating_address_table(addr, account_resource, new_auth_key);
+
entry fun rotate_authentication_key_call(account: &signer, new_auth_key: vector<u8>) acquires Account {
+    rotate_authentication_key_internal(account, new_auth_key);
 }
 
@@ -2526,29 +2526,15 @@ The length of new_auth_key is 32.
-The Account existed under the signer before the call. -The length of new_auth_key is 32.
let addr = signer::address_of(account);
+// This enforces high-level requirement 10:
+let post account_resource = global<Account>(addr);
 aborts_if !exists<Account>(addr);
 aborts_if vector::length(new_auth_key) != 32;
-let account_resource = global<Account>(addr);
-let curr_auth_key = from_bcs::deserialize<address>(account_resource.authentication_key);
-let originating_addr = addr;
-let address_map = global<OriginatingAddress>(@aptos_framework).address_map;
-let new_auth_key_addr = from_bcs::deserialize<address>(new_auth_key);
-aborts_if !exists<OriginatingAddress>(@aptos_framework);
-aborts_if !from_bcs::deserializable<address>(account_resource.authentication_key);
-aborts_if table::spec_contains(address_map, curr_auth_key) &&
-    table::spec_get(address_map, curr_auth_key) != originating_addr;
-aborts_if curr_auth_key != new_auth_key_addr && table::spec_contains(address_map, new_auth_key_addr);
-include UpdateAuthKeyAndOriginatingAddressTableAbortsIf {
-    originating_addr: addr,
-    new_auth_key_vector: new_auth_key,
-};
-let post auth_key = global<Account>(addr).authentication_key;
-ensures auth_key == new_auth_key;
+modifies global<Account>(addr);
+ensures account_resource.authentication_key == new_auth_key;
 
diff --git a/aptos-move/framework/aptos-framework/sources/account.move b/aptos-move/framework/aptos-framework/sources/account.move index 76bea0e3132c5..807b09c9d4284 100644 --- a/aptos-move/framework/aptos-framework/sources/account.move +++ b/aptos-move/framework/aptos-framework/sources/account.move @@ -265,8 +265,11 @@ module aptos_framework::account { borrow_global(addr).authentication_key } - /// This function is used to rotate a resource account's authentication key to 0, so that no private key can control - /// the resource account. + /// This function is used to rotate a resource account's authentication key to `new_auth_key`. This is done in + /// many contexts: + /// 1. During normal key rotation via `rotate_authentication_key` or `rotate_authentication_key_call` + /// 2. During resource account initialization so that no private key can control the resource account + /// 3. During multisig_v2 account creation public(friend) fun rotate_authentication_key_internal(account: &signer, new_auth_key: vector) acquires Account { let addr = signer::address_of(account); assert!(exists_at(addr), error::not_found(EACCOUNT_DOES_NOT_EXIST)); @@ -278,16 +281,13 @@ module aptos_framework::account { account_resource.authentication_key = new_auth_key; } - /// Entry function-only rotation key function that allows the signer update their authentication_key. - entry fun rotate_authentication_key_call(account: &signer, new_auth_key: vector) acquires Account, OriginatingAddress { - let addr = signer::address_of(account); - assert!(exists_at(addr), error::not_found(EACCOUNT_DOES_NOT_EXIST)); - assert!( - vector::length(&new_auth_key) == 32, - error::invalid_argument(EMALFORMED_AUTHENTICATION_KEY) - ); - let account_resource = borrow_global_mut(addr); - update_auth_key_and_originating_address_table(addr, account_resource, new_auth_key); + /// Private entry function for key rotation that allows the signer to update their authentication key. + /// Note that this does not update the `OriginatingAddress` table because the `new_auth_key` is not "verified": it + /// does not come with a proof-of-knowledge of the underlying SK. Nonetheless, we need this functionality due to + /// the introduction of non-standard key algorithms, such as passkeys, which cannot produce proofs-of-knowledge in + /// the format expected in `rotate_authentication_key`. + entry fun rotate_authentication_key_call(account: &signer, new_auth_key: vector) acquires Account { + rotate_authentication_key_internal(account, new_auth_key); } /// Generic authentication key rotation function that allows the user to rotate their authentication key from any scheme to any scheme. @@ -1337,7 +1337,7 @@ module aptos_framework::account { #[test(account = @aptos_framework)] - public entry fun test_simple_rotation(account: &signer) acquires Account, OriginatingAddress { + public entry fun test_simple_rotation(account: &signer) acquires Account { initialize(account); let alice_addr = @0x1234; @@ -1349,10 +1349,6 @@ module aptos_framework::account { let new_addr = from_bcs::to_address(new_auth_key); rotate_authentication_key_call(&alice, new_auth_key); - - let address_map = &mut borrow_global_mut(@aptos_framework).address_map; - let expected_originating_address = table::borrow(address_map, new_addr); - assert!(*expected_originating_address == alice_addr, 0); assert!(borrow_global(alice_addr).authentication_key == new_auth_key, 0); } diff --git a/aptos-move/framework/aptos-framework/sources/account.spec.move b/aptos-move/framework/aptos-framework/sources/account.spec.move index 91e1aca7751ce..2b80dbdce527d 100644 --- a/aptos-move/framework/aptos-framework/sources/account.spec.move +++ b/aptos-move/framework/aptos-framework/sources/account.spec.move @@ -203,35 +203,14 @@ spec aptos_framework::account { ensures account_resource.authentication_key == new_auth_key; } - /// The Account existed under the signer before the call. - /// The length of new_auth_key is 32. spec rotate_authentication_key_call(account: &signer, new_auth_key: vector) { let addr = signer::address_of(account); + /// [high-level-req-10] + let post account_resource = global(addr); aborts_if !exists(addr); aborts_if vector::length(new_auth_key) != 32; - let account_resource = global(addr); - let curr_auth_key = from_bcs::deserialize
(account_resource.authentication_key); - - // Verify all properties in update_auth_key_and_originating_address_table - let originating_addr = addr; - - let address_map = global(@aptos_framework).address_map; - let new_auth_key_addr = from_bcs::deserialize
(new_auth_key); - - aborts_if !exists(@aptos_framework); - aborts_if !from_bcs::deserializable
(account_resource.authentication_key); - aborts_if table::spec_contains(address_map, curr_auth_key) && - table::spec_get(address_map, curr_auth_key) != originating_addr; - - aborts_if curr_auth_key != new_auth_key_addr && table::spec_contains(address_map, new_auth_key_addr); - - include UpdateAuthKeyAndOriginatingAddressTableAbortsIf { - originating_addr: addr, - new_auth_key_vector: new_auth_key, - }; - - let post auth_key = global(addr).authentication_key; - ensures auth_key == new_auth_key; + modifies global(addr); + ensures account_resource.authentication_key == new_auth_key; } spec fun spec_assert_valid_rotation_proof_signature_and_get_auth_key(scheme: u8, public_key_bytes: vector, signature: vector, challenge: RotationProofChallenge): vector; diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs index 27a318e48603d..045a27c1a664a 100644 --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs @@ -131,7 +131,11 @@ pub enum EntryFunctionCall { cap_update_table: Vec, }, - /// Entry function-only rotation key function that allows the signer update their authentication_key. + /// Private entry function for key rotation that allows the signer to update their authentication key. + /// Note that this does not update the `OriginatingAddress` table because the `new_auth_key` is not "verified": it + /// does not come with a proof-of-knowledge of the underlying SK. Nonetheless, we need this functionality due to + /// the introduction of non-standard key algorithms, such as passkeys, which cannot produce proofs-of-knowledge in + /// the format expected in `rotate_authentication_key`. AccountRotateAuthenticationKeyCall { new_auth_key: Vec, }, @@ -1676,7 +1680,11 @@ pub fn account_rotate_authentication_key( )) } -/// Entry function-only rotation key function that allows the signer update their authentication_key. +/// Private entry function for key rotation that allows the signer to update their authentication key. +/// Note that this does not update the `OriginatingAddress` table because the `new_auth_key` is not "verified": it +/// does not come with a proof-of-knowledge of the underlying SK. Nonetheless, we need this functionality due to +/// the introduction of non-standard key algorithms, such as passkeys, which cannot produce proofs-of-knowledge in +/// the format expected in `rotate_authentication_key`. pub fn account_rotate_authentication_key_call(new_auth_key: Vec) -> TransactionPayload { TransactionPayload::EntryFunction(EntryFunction::new( ModuleId::new(