Skip to content

x-chain DAA authentication using Sign Personal Message with Sui

AIP-124 - x-chain DAA authentication using Sign Personal Message with Sui

Section titled “AIP-124 - x-chain DAA authentication using Sign Personal Message with Sui”

Implements authentication function giving Sui users native Aptos accounts, and allowing them to sign transactions on Aptos blockchain, directly from their Sui wallet. This is done using derivable account abstraction (AIP-113).

Allow Sui accounts (Sui Ed25519 key pairs) to create Aptos transactions, and be able to authenticate and execute them directly on the Aptos blockchain, without interaction with the Sui blockchain. We do so by utilizing “Sign Personal Message” specification, and defining a message format that gives enough details to users so they understand what they are signing reasonably well.

This will allow users with Sui Wallets to be able to interact with the Aptos blockchain.

Alternatively, wallets from other chains can implement integration with Aptos natively, providing more seamless experience to the users.

The dApp is required to issue a signPersonalMessage request to the Sui wallet with:

<domain> wants you to sign in with your Sui account:
<sui_address>
Please confirm you explicitly initiated this request from <domain>. You are approving to execute transaction <entry_function> on Aptos blockchain (<network_name>).
Nonce: <aptos_txn_digest>

And submit the transaction to Aptos with its authenticator set to the following struct:

AccountAuthenticator::Abstraction {
function_info: FunctionInfo::from("0x1::sui_derivable_account::authenticate"),
auth_data: AbstractionAuthData::DerivableV1 {
signing_message_digest: aptos_txn_digest,
abstract_signature: bcs::to_bytes(SuiAbstractSignature::SuiDerivedSignature {
signature: sign_personal_message_signature,
}),
abstract_public_key: bcs::to_bytes(SuiAbstractPublicKey {
sui_account_address,
domain,
}),
},
}

with Aptos types:

enum SuiAbstractSignature has drop {
SuiDerivedSignature {
signature: vector<u8>,
},
}
struct SuiAbstractPublicKey has drop {
sui_account_address: vector<u8>,
domain: vector<u8>,
}

If the transaction is signed correctly, as indicated above, its signature will verify successfully via 0x1::sui_derivable_account::authenticate for the Aptos account whose address is derived via account_abstraction::derive_account_address(function_info, abstract_public_key)

0x1::sui_derivable_account::authenticate will:

  1. Re-construct the expected text message
  2. Extract the public_key, which is part of the sign_personal_message_signature
  3. Derive the account_address from the public_key
  4. Verify that the account_address matches with the one from the abstract_public_key.
  5. In addition, it will verify that sign_personal_message_signature from abstract_signature is a correct signature of that message.

Looking at what each field in the message the Sui’s wallet displays is and what is it used for:

  • domain is the website domain (e.g., example.com) that issued the signing request to the Sui wallet. The wallet itself is responsible for ensuring this field is populated correctly.. This sandboxes each domain into separate Aptos account, providing better isolation and security.
  • sui_address is Sui wallet account address including the 0x prefix.
  • entry_function is a string representation of an Aptos entry function transaction will execute. It contains address, module name and function name, for example 0x1::primary_fungible_store::transfer. This restricts usage to only entry functions (i.e. preventing scripts, etc), but is made required as it is the main human-readable information about the transaction that user can read and understand.
  • network_name is a string representation of the Aptos chain ID on which transaction can be executed on, with: “mainnet” for 1, “testnet” for 2, “local” for 4, and “custom network <chain_id>” for others
  • aptos_txn_digest is the digest of the Aptos transaction that you want to execute. All details about the transaction itself, and replay protection is inside of the transaction. Note: aptos_txn_digest is guaranteed to be unique on Aptos, so it can be used raw directly as a nonce. Alternatively, it could’ve been designed to have aptos_txn_digest inside the message, and nonce be a separate user-controlled nonce, but that seems unnecessary.

Verification implementation is:

public fun authenticate(account: signer, aa_auth_data: AbstractionAuthData): signer {
let abstract_signature = deserialize_abstract_signature(aa_auth_data.derivable_abstract_signature());
let (signing_scheme, abstract_signature_signature, abstract_signature_public_key) = split_signature_bytes(&abstract_signature.signature);
// Check siging scheme is ED25519 as we currently only support this scheme
assert!(get_signing_scheme(signing_scheme) == SuiSigningScheme::ED25519, EINVALID_SIGNING_SCHEME_TYPE);
// Derive the account address from the public key
let sui_account_address = derive_account_address_from_public_key(signing_scheme, abstract_signature_public_key);
let derivable_abstract_public_key = aa_auth_data.derivable_abstract_public_key();
let abstract_public_key = deserialize_abstract_public_key(derivable_abstract_public_key);
// Check the account address matches the abstract public key
assert!(&sui_account_address == &abstract_public_key.sui_account_address, EACCOUNT_ADDRESS_MISMATCH);
let public_key = new_validated_public_key_from_bytes(abstract_signature_public_key);
assert!(public_key.is_some(), EINVALID_PUBLIC_KEY);
let digest_utf8 = string_utils::to_string(aa_auth_data.digest()).bytes();
// Build the raw message
let raw_message = construct_message(&sui_account_address, &abstract_public_key.domain, entry_function_name, digest_utf8);
// Prepend Intent to the message
let intent = Intent {
scope: PersonalMessage,
version: V0,
app_id: Sui,
};
let msg = IntentMessage {
intent,
value: raw_message,
};
// Serialize the whole struct
let bcs_bytes = bcs::to_bytes<IntentMessage>(&msg);
// Hash full_message with blake2b256
let hash = aptos_hash::blake2b_256(bcs_bytes);
let signature = new_signature_from_bytes(abstract_signature_signature);
assert!(
ed25519::signature_verify_strict(
&signature,
&public_key_into_unvalidated(public_key.destroy_some()),
hash,
),
EINVALID_SIGNATURE
);
}

https://github.com/aptos-labs/aptos-core/pull/16830

Verified payload signed with signPersonalMessage with Slush is verified correctly and passes authorization

Security Considerations, Risks and Drawbacks

Section titled “Security Considerations, Risks and Drawbacks”
  • domain will not be verified, and it is on the user to understand risks. For that, we include in the message to “Please confirm you explicitly initiated this request from
  • Using this flow doesn’t provide “simulation” information to the user (dapp could run simulation, but cannot be forced to), so user has less information about what is being signed. For that, direct wallet support would be needed.
  • Without simulation, entry function is placed inside the message, to help user understand better what it is authorizing. This is a balance between given user too much information (so it doesn’t review it closely), like full json representation of the Aptos transaction, and having users sign without much information. It is a question if adding arguments to the entry function within the message itself is useful, they can give more context, but can also be quite large and hardly understandable.
  • Note that actual address of the module where entry function is will be printed, while a lot UIs (explorer, wallet, etc) might maintain list of common ecosystem projects and addresses, and give users more easily understandable name. In order to do so here, we would need to maintain such list onchain, so there are tradeoffs to be considered.
  • Currently, we only support Ed25519 signing scheme, tho adding support to more schemes is doable.
  • SuiAbstractSignature is enum, so that we can support new message formats in the future. For example, we could add list of permissions from AIP-103 to be listed, giving a restricted access to the transaction itself
    • it also allows us, in case a security issue is found, to deprecate current message format while keeping access to the account

TBD

Suggested developer platform support timeline

Section titled “Suggested developer platform support timeline”

TBD

TBD