Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,14 @@ module aptos_framework::account {
use aptos_std::multi_ed25519;
use aptos_std::single_key;
use aptos_std::multi_key;
use aptos_std::single_key::AnyPublicKey;
use aptos_std::table::{Self, Table};
use aptos_std::type_info::{Self, TypeInfo};

friend aptos_framework::aptos_account;
friend aptos_framework::coin;
friend aptos_framework::genesis;
friend aptos_framework::keyless_account;
friend aptos_framework::multisig_account;
friend aptos_framework::resource_account;
friend aptos_framework::transaction_validation;
Expand Down Expand Up @@ -495,9 +497,9 @@ module aptos_framework::account {
});
}

/// Upserts an ED25519 backup key to an account that has a keyless public key as its original public key by converting the account's authentication key
/// Upserts an Ed25519 backup key to an account that has a keyless public key as its original public key by converting the account's authentication key
/// to a multi-key of the original keyless public key and the new backup key that requires 1 signature from either key to authenticate.
/// This function takes a the account's original keyless public key and a ED25519 backup public key and rotates the account's authentication key to a multi-key of
/// This function takes the account's original keyless public key and a Ed25519 backup public key and rotates the account's authentication key to a multi-key of
/// the original keyless public key and the new backup key that requires 1 signature from either key to authenticate.
///
/// Note: This function emits a `KeyRotationToMultiPublicKey` event marking both keys as verified since the keyless public key
Expand All @@ -506,7 +508,7 @@ module aptos_framework::account {
/// # Arguments
/// * `account` - The signer representing the keyless account
/// * `keyless_public_key` - The original keyless public key of the account (wrapped in an AnyPublicKey)
/// * `backup_public_key` - The ED25519 public key to add as a backup
/// * `backup_public_key` - The Ed25519 public key to add as a backup
/// * `backup_key_proof` - A signature from the backup key proving ownership
///
/// # Aborts
Expand All @@ -518,8 +520,27 @@ module aptos_framework::account {
/// # Events
/// * Emits a `KeyRotationToMultiPublicKey` event with the new multi-key configuration
entry fun upsert_ed25519_backup_key_on_keyless_account(account: &signer, keyless_public_key: vector<u8>, backup_public_key: vector<u8>, backup_key_proof: vector<u8>) acquires Account {
// Check that the provided public key is a keyless public key
let keyless_single_key = single_key::new_public_key_from_bytes(keyless_public_key);

upsert_ed25519_backup_key_on_keyless_account_internal(
account,
keyless_single_key,
backup_public_key,
backup_key_proof
)
}

/// Arguments:
/// keyless_single_key expected to be an `AnyPublicKey::Keyless` or an `AnyPublicKey::FederatedKeyless`
///
/// Note: The way this was written makes it hard to trivially change it to use the more type-safe `keyless::PublicKey` rather than `AnyPublicKey`
public(friend) fun upsert_ed25519_backup_key_on_keyless_account_internal(
account: &signer,
keyless_single_key: AnyPublicKey,
backup_public_key: vector<u8>,
backup_key_proof: vector<u8>
) acquires Account {
// Check that the provided public key is a keyless public key
assert!(single_key::is_keyless_or_federated_keyless_public_key(&keyless_single_key), error::invalid_argument(ENOT_A_KEYLESS_PUBLIC_KEY));

let addr = signer::address_of(account);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@
module aptos_framework::keyless_account {
use std::bn254_algebra;
use std::config_buffer;
use std::error;
use std::option::Option;
use std::signer;
use std::string::String;
use aptos_std::crypto_algebra;
use aptos_std::ed25519;
use aptos_std::single_key;
use aptos_framework::account;
use aptos_framework::chain_status;
use aptos_framework::system_addresses;

Expand All @@ -23,6 +26,13 @@ module aptos_framework::keyless_account {
/// A serialized BN254 G2 point is invalid.
const E_INVALID_BN254_G2_SERIALIZATION: u64 = 3;

/// The encrypted payload exceeds `MAX_ENCRYPTED_PAYLOAD_BYTES`.
const E_ENCRYPTED_PAYLOAD_TOO_LONG: u64 = 4;

/// Maximum size of the encrypted payload stored in `KeylessAccountStorage`.
/// 256 bytes is generous for an encrypted 32-byte DK (key + nonce + auth tag).
const MAX_ENCRYPTED_PAYLOAD_BYTES: u64 = 256;

#[resource_group(scope = global)]
struct Group {}

Expand Down Expand Up @@ -132,6 +142,14 @@ module aptos_framework::keyless_account {
max_jwt_header_b64_bytes: u32,
}

/// An on-chain struct for keyless accounts to store various data in. Initial use case is for storing an encryption
/// of their confidential asset decryption key.
enum KeylessAccountStorage has key, store, drop {
V1 {
encrypted_payload: vector<u8>
}
}

#[test_only]
public fun initialize_for_test(fx: &signer, vk: Groth16VerificationKey, constants: Configuration) {
system_addresses::assert_aptos_framework(fx);
Expand Down Expand Up @@ -211,6 +229,48 @@ module aptos_framework::keyless_account {
move_to(fx, config);
}

/// Atomically performs an `account::upsert_ed25519_backup_key_on_keyless_account` and stores an encrypted payload
/// for the user. Currently used for backing up decryption keys on-chain in Petra for keyless accounts.
entry fun upsert_ed25519_backup_key_on_keyless_account_with_encrypted_payload(
account: &signer,
keyless_public_key: vector<u8>,
backup_public_key: vector<u8>,
backup_key_proof: vector<u8>,
encrypted_payload: vector<u8>
) acquires KeylessAccountStorage {
// Step 1: Perform the key rotation/installation
let keyless_single_key = single_key::new_public_key_from_bytes(keyless_public_key);

account::upsert_ed25519_backup_key_on_keyless_account_internal(
account,
keyless_single_key,
backup_public_key,
backup_key_proof
);

// Step 2: Validate and store the encrypted payload
assert!(
encrypted_payload.length() <= MAX_ENCRYPTED_PAYLOAD_BYTES,
error::invalid_argument(E_ENCRYPTED_PAYLOAD_TOO_LONG)
);
let addr = signer::address_of(account);
if (!exists<KeylessAccountStorage>(addr)) {
move_to(account, KeylessAccountStorage::V1 { encrypted_payload });
} else {
KeylessAccountStorage[addr].encrypted_payload = encrypted_payload;
};
}

#[view]
/// Returns the encrypted payload stored for `addr`, or an empty vector if none exists.
public fun get_encrypted_payload(addr: address): vector<u8> acquires KeylessAccountStorage {
if (exists<KeylessAccountStorage>(addr)) {
KeylessAccountStorage[addr].encrypted_payload
} else {
vector[]
}
}

#[deprecated]
public fun update_training_wheels(fx: &signer, pk: Option<vector<u8>>) acquires Configuration {
system_addresses::assert_aptos_framework(fx);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ module aptos_std::keyless {
// Structs
//

/// An *unvalidated* any public key: not necessarily an elliptic curve point, just a sequence of 32 bytes
/// An *unvalidated* keyless public key
struct PublicKey has copy, drop, store {
iss: String,
idc: vector<u8>
Expand Down
Loading