@@ -9,6 +9,7 @@ module aptos_framework::account {
9
9
use aptos_framework::create_signer::create_signer;
10
10
use aptos_framework::event::{Self , EventHandle };
11
11
use aptos_framework::guid;
12
+ use aptos_framework::permissioned_signer;
12
13
use aptos_framework::system_addresses;
13
14
use aptos_std::ed25519;
14
15
use aptos_std::from_bcs;
@@ -179,6 +180,8 @@ module aptos_framework::account {
179
180
const ENEW_AUTH_KEY_ALREADY_MAPPED : u64 = 21 ;
180
181
/// The current authentication key and the new authentication key are the same
181
182
const ENEW_AUTH_KEY_SAME_AS_CURRENT : u64 = 22 ;
183
+ /// Current permissioned signer cannot perform the privilaged operations.
184
+ const ENO_ACCOUNT_PERMISSION : u64 = 23 ;
182
185
183
186
/// Explicitly separate the GUID space between Object and Account to prevent accidental overlap.
184
187
const MAX_GUID_CREATION_NUM : u64 = 0x4000000000000 ;
@@ -187,6 +190,43 @@ module aptos_framework::account {
187
190
/// Create signer for testing, independently of an Aptos-style `Account`.
188
191
public fun create_signer_for_test (addr: address ): signer { create_signer (addr) }
189
192
193
+ enum AccountPermission has copy , drop , store {
194
+ /// Permission to rotate a key.
195
+ KeyRotation ,
196
+ /// Permission to offer another address to act like your address
197
+ Offering ,
198
+ }
199
+
200
+ /// Permissions
201
+ ///
202
+ inline fun check_rotation_permission (s: &signer ) {
203
+ assert !(
204
+ permissioned_signer::check_permission_exists (s, AccountPermission::KeyRotation {}),
205
+ error::permission_denied (ENO_ACCOUNT_PERMISSION ),
206
+ );
207
+ }
208
+
209
+ inline fun check_offering_permission (s: &signer ) {
210
+ assert !(
211
+ permissioned_signer::check_permission_exists (s, AccountPermission::Offering {}),
212
+ error::permission_denied (ENO_ACCOUNT_PERMISSION ),
213
+ );
214
+ }
215
+
216
+ /// Grant permission to perform key rotations on behalf of the master signer.
217
+ ///
218
+ /// This is **extremely dangerous** and should be granted only when it's absolutely needed.
219
+ public fun grant_key_rotation_permission (master: &signer , permissioned_signer: &signer ) {
220
+ permissioned_signer::authorize_unlimited (master, permissioned_signer, AccountPermission::KeyRotation {})
221
+ }
222
+
223
+ /// Grant permission to use offered address's signer on behalf of the master signer.
224
+ ///
225
+ /// This is **extremely dangerous** and should be granted only when it's absolutely needed.
226
+ public fun grant_key_offering_permission (master: &signer , permissioned_signer: &signer ) {
227
+ permissioned_signer::authorize_unlimited (master, permissioned_signer, AccountPermission::Offering {})
228
+ }
229
+
190
230
/// Only called during genesis to initialize system resources for this module.
191
231
public (friend ) fun initialize (aptos_framework: &signer ) {
192
232
system_addresses::assert_aptos_framework (aptos_framework);
@@ -302,6 +342,7 @@ module aptos_framework::account {
302
342
vector ::length (&new_auth_key) == 32 ,
303
343
error::invalid_argument (EMALFORMED_AUTHENTICATION_KEY )
304
344
);
345
+ check_rotation_permission (account);
305
346
let account_resource = borrow_global_mut <Account >(addr);
306
347
account_resource.authentication_key = new_auth_key;
307
348
}
@@ -357,6 +398,7 @@ module aptos_framework::account {
357
398
) acquires Account , OriginatingAddress {
358
399
let addr = signer ::address_of (account);
359
400
assert !(exists_at (addr), error::not_found (EACCOUNT_DOES_NOT_EXIST ));
401
+ check_rotation_permission (account);
360
402
let account_resource = borrow_global_mut <Account >(addr);
361
403
362
404
// Verify the given `from_public_key_bytes` matches this account's current authentication key.
@@ -412,6 +454,7 @@ module aptos_framework::account {
412
454
new_public_key_bytes: vector <u8 >,
413
455
cap_update_table: vector <u8 >
414
456
) acquires Account , OriginatingAddress {
457
+ check_rotation_permission (delegate_signer);
415
458
assert !(exists_at (rotation_cap_offerer_address), error::not_found (EOFFERER_ADDRESS_DOES_NOT_EXIST ));
416
459
417
460
// Check that there exists a rotation capability offer at the offerer's account resource for the delegate.
@@ -471,6 +514,7 @@ module aptos_framework::account {
471
514
account_public_key_bytes: vector <u8 >,
472
515
recipient_address: address ,
473
516
) acquires Account {
517
+ check_rotation_permission (account);
474
518
let addr = signer ::address_of (account);
475
519
assert !(exists_at (recipient_address), error::not_found (EACCOUNT_DOES_NOT_EXIST ));
476
520
@@ -569,6 +613,7 @@ module aptos_framework::account {
569
613
/// Revoke the rotation capability offer given to `to_be_revoked_recipient_address` from `account`
570
614
public entry fun revoke_rotation_capability (account: &signer , to_be_revoked_address: address ) acquires Account {
571
615
assert !(exists_at (to_be_revoked_address), error::not_found (EACCOUNT_DOES_NOT_EXIST ));
616
+ check_rotation_permission (account);
572
617
let addr = signer ::address_of (account);
573
618
let account_resource = borrow_global <Account >(addr);
574
619
assert !(
@@ -580,6 +625,7 @@ module aptos_framework::account {
580
625
581
626
/// Revoke any rotation capability offer in the specified account.
582
627
public entry fun revoke_any_rotation_capability (account: &signer ) acquires Account {
628
+ check_rotation_permission (account);
583
629
let account_resource = borrow_global_mut <Account >(signer ::address_of (account));
584
630
option::extract (&mut account_resource.rotation_capability_offer.for);
585
631
}
@@ -600,6 +646,7 @@ module aptos_framework::account {
600
646
account_public_key_bytes: vector <u8 >,
601
647
recipient_address: address
602
648
) acquires Account {
649
+ check_offering_permission (account);
603
650
let source_address = signer ::address_of (account);
604
651
assert !(exists_at (recipient_address), error::not_found (EACCOUNT_DOES_NOT_EXIST ));
605
652
@@ -639,6 +686,7 @@ module aptos_framework::account {
639
686
/// has a signer capability offer from `account` but will be revoked in this function).
640
687
public entry fun revoke_signer_capability (account: &signer , to_be_revoked_address: address ) acquires Account {
641
688
assert !(exists_at (to_be_revoked_address), error::not_found (EACCOUNT_DOES_NOT_EXIST ));
689
+ check_offering_permission (account);
642
690
let addr = signer ::address_of (account);
643
691
let account_resource = borrow_global <Account >(addr);
644
692
assert !(
@@ -650,13 +698,15 @@ module aptos_framework::account {
650
698
651
699
/// Revoke any signer capability offer in the specified account.
652
700
public entry fun revoke_any_signer_capability (account: &signer ) acquires Account {
701
+ check_offering_permission (account);
653
702
let account_resource = borrow_global_mut <Account >(signer ::address_of (account));
654
703
option::extract (&mut account_resource.signer_capability_offer.for);
655
704
}
656
705
657
706
/// Return an authorized signer of the offerer, if there's an existing signer capability offer for `account`
658
707
/// at the offerer's address.
659
708
public fun create_authorized_signer (account: &signer , offerer_address: address ): signer acquires Account {
709
+ check_offering_permission (account);
660
710
assert !(exists_at (offerer_address), error::not_found (EOFFERER_ADDRESS_DOES_NOT_EXIST ));
661
711
662
712
// Check if there's an existing signer capability offer from the offerer.
@@ -1202,6 +1252,123 @@ module aptos_framework::account {
1202
1252
assert !(signer ::address_of (&signer ) == signer ::address_of (&alice), 0 );
1203
1253
}
1204
1254
1255
+ #[test(bob = @0x345 )]
1256
+ public entry fun test_valid_check_signer_capability_and_create_authorized_signer_with_permission (bob: signer ) acquires Account {
1257
+ let (alice_sk, alice_pk) = ed25519::generate_keys ();
1258
+ let alice_pk_bytes = ed25519::validated_public_key_to_bytes (&alice_pk);
1259
+ let alice = create_account_from_ed25519_public_key (alice_pk_bytes);
1260
+ let alice_addr = signer ::address_of (&alice);
1261
+
1262
+ let bob_addr = signer ::address_of (&bob);
1263
+ create_account (bob_addr);
1264
+
1265
+ let challenge = SignerCapabilityOfferProofChallengeV2 {
1266
+ sequence_number: borrow_global <Account >(alice_addr).sequence_number,
1267
+ source_address: alice_addr,
1268
+ recipient_address: bob_addr,
1269
+ };
1270
+
1271
+ let alice_signer_capability_offer_sig = ed25519::sign_struct (&alice_sk, challenge);
1272
+
1273
+ let alice_permission_handle = permissioned_signer::create_permissioned_handle (&alice);
1274
+ let alice_permission_signer = permissioned_signer::signer_from_permissioned_handle (&alice_permission_handle);
1275
+
1276
+ grant_key_offering_permission (&alice, &alice_permission_signer);
1277
+
1278
+ offer_signer_capability (
1279
+ &alice_permission_signer,
1280
+ ed25519::signature_to_bytes (&alice_signer_capability_offer_sig),
1281
+ 0 ,
1282
+ alice_pk_bytes,
1283
+ bob_addr
1284
+ );
1285
+
1286
+ assert !(option::contains (&borrow_global <Account >(alice_addr).signer_capability_offer.for, &bob_addr), 0 );
1287
+
1288
+ let signer = create_authorized_signer (&bob, alice_addr);
1289
+ assert !(signer ::address_of (&signer ) == signer ::address_of (&alice), 0 );
1290
+
1291
+ permissioned_signer::destroy_permissioned_handle (alice_permission_handle);
1292
+ }
1293
+
1294
+ #[test(bob = @0x345 )]
1295
+ #[expected_failure(abort_code = 0x50017 , location = Self)]
1296
+ public entry fun test_valid_check_signer_capability_and_create_authorized_signer_with_no_permission (bob: signer ) acquires Account {
1297
+ let (alice_sk, alice_pk) = ed25519::generate_keys ();
1298
+ let alice_pk_bytes = ed25519::validated_public_key_to_bytes (&alice_pk);
1299
+ let alice = create_account_from_ed25519_public_key (alice_pk_bytes);
1300
+ let alice_addr = signer ::address_of (&alice);
1301
+
1302
+ let bob_addr = signer ::address_of (&bob);
1303
+ create_account (bob_addr);
1304
+
1305
+ let challenge = SignerCapabilityOfferProofChallengeV2 {
1306
+ sequence_number: borrow_global <Account >(alice_addr).sequence_number,
1307
+ source_address: alice_addr,
1308
+ recipient_address: bob_addr,
1309
+ };
1310
+
1311
+ let alice_signer_capability_offer_sig = ed25519::sign_struct (&alice_sk, challenge);
1312
+
1313
+ let alice_permission_handle = permissioned_signer::create_permissioned_handle (&alice);
1314
+ let alice_permission_signer = permissioned_signer::signer_from_permissioned_handle (&alice_permission_handle);
1315
+
1316
+ offer_signer_capability (
1317
+ &alice_permission_signer,
1318
+ ed25519::signature_to_bytes (&alice_signer_capability_offer_sig),
1319
+ 0 ,
1320
+ alice_pk_bytes,
1321
+ bob_addr
1322
+ );
1323
+
1324
+ assert !(option::contains (&borrow_global <Account >(alice_addr).signer_capability_offer.for, &bob_addr), 0 );
1325
+
1326
+ let signer = create_authorized_signer (&bob, alice_addr);
1327
+ assert !(signer ::address_of (&signer ) == signer ::address_of (&alice), 0 );
1328
+
1329
+ permissioned_signer::destroy_permissioned_handle (alice_permission_handle);
1330
+ }
1331
+
1332
+ #[test(bob = @0x345 )]
1333
+ #[expected_failure(abort_code = 0x50017 , location = Self)]
1334
+ public entry fun test_valid_check_signer_capability_and_create_authorized_signer_with_wrong_permission (bob: signer ) acquires Account {
1335
+ let (alice_sk, alice_pk) = ed25519::generate_keys ();
1336
+ let alice_pk_bytes = ed25519::validated_public_key_to_bytes (&alice_pk);
1337
+ let alice = create_account_from_ed25519_public_key (alice_pk_bytes);
1338
+ let alice_addr = signer ::address_of (&alice);
1339
+
1340
+ let bob_addr = signer ::address_of (&bob);
1341
+ create_account (bob_addr);
1342
+
1343
+ let challenge = SignerCapabilityOfferProofChallengeV2 {
1344
+ sequence_number: borrow_global <Account >(alice_addr).sequence_number,
1345
+ source_address: alice_addr,
1346
+ recipient_address: bob_addr,
1347
+ };
1348
+
1349
+ let alice_signer_capability_offer_sig = ed25519::sign_struct (&alice_sk, challenge);
1350
+
1351
+ let alice_permission_handle = permissioned_signer::create_permissioned_handle (&alice);
1352
+ let alice_permission_signer = permissioned_signer::signer_from_permissioned_handle (&alice_permission_handle);
1353
+
1354
+ grant_key_rotation_permission (&alice, &alice_permission_signer);
1355
+
1356
+ offer_signer_capability (
1357
+ &alice_permission_signer,
1358
+ ed25519::signature_to_bytes (&alice_signer_capability_offer_sig),
1359
+ 0 ,
1360
+ alice_pk_bytes,
1361
+ bob_addr
1362
+ );
1363
+
1364
+ assert !(option::contains (&borrow_global <Account >(alice_addr).signer_capability_offer.for, &bob_addr), 0 );
1365
+
1366
+ let signer = create_authorized_signer (&bob, alice_addr);
1367
+ assert !(signer ::address_of (&signer ) == signer ::address_of (&alice), 0 );
1368
+
1369
+ permissioned_signer::destroy_permissioned_handle (alice_permission_handle);
1370
+ }
1371
+
1205
1372
#[test(bob = @0x345 )]
1206
1373
public entry fun test_get_signer_cap_and_is_signer_cap (bob: signer ) acquires Account {
1207
1374
let (alice_sk, alice_pk) = ed25519::generate_keys ();
0 commit comments