Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Module 0x1::sui_derivable_account

Derivable account abstraction that verifies a message signed by Sui wallet.

  1. The message format is as follows:

wants you to sign in with your Sui account: <sui_account_address>

Please confirm you explicitly initiated this request from . You are approving to execute transaction <entry_function_name> on Aptos blockchain (<network_name>).

Nonce:

  1. The abstract public key is a BCS serialized SuiAbstractPublicKey.
  2. The abstract signature is a BCS serialized SuiAbstractSignature.
  3. This module has been tested for the following wallets:
use 0x1::aptos_hash;
use 0x1::auth_data;
use 0x1::bcs;
use 0x1::bcs_stream;
use 0x1::common_account_abstractions_utils;
use 0x1::ed25519;
use 0x1::option;
use 0x1::string;
use 0x1::string_utils;
use 0x1::transaction_context;
use 0x1::vector;

Enum SuiAbstractSignature

enum SuiAbstractSignature has drop
Variants
MessageV1
Fields
signature: vector<u8>
The signature of the message in raw bytes

Struct SuiAbstractPublicKey

Sui abstract public key defined with the

struct SuiAbstractPublicKey has drop
Fields
sui_account_address: vector<u8>
domain: vector<u8>

Enum SuiSigningScheme

Sui signing scheme as defined in https://github.com/MystenLabs/ts-sdks/blob/main/packages/typescript/src/cryptography/signature-scheme.ts#L19

enum SuiSigningScheme has drop
Variants
ED25519
Fields

Struct IntentMessage

A wrapper struct that defines a message with its signing context (intent). https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L168

struct IntentMessage has copy, drop, store
Fields
intent: sui_derivable_account::Intent
value: vector<u8>

Struct Intent

Metadata specifying the scope, version, and application domain of the message. https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L86

struct Intent has copy, drop, store
Fields
scope: sui_derivable_account::IntentScope
version: sui_derivable_account::IntentVersion
app_id: sui_derivable_account::AppId

Enum IntentScope

https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L60

enum IntentScope has copy, drop, store
Variants
TransactionData
Fields
TransactionEffects
Fields
CheckpointSummary
Fields
PersonalMessage
Fields

Enum IntentVersion

https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L18

enum IntentVersion has copy, drop, store
Variants
V0
Fields

Enum AppId

https://github.com/MystenLabs/sui/blob/main/crates/shared-crypto/src/intent.rs#L35

enum AppId has copy, drop, store
Variants
Sui
Fields

Constants

Malformed data with trailing bytes.

const EMALFORMED_DATA: u64 = 8;

Function is deprecated and should not be called.

const EDEPRECATED: u64 = 9;

Invalid public key.

const EINVALID_PUBLIC_KEY: u64 = 6;

Entry function payload is missing.

const EMISSING_ENTRY_FUNCTION_PAYLOAD: u64 = 1;

Invalid signature.

const EINVALID_SIGNATURE: u64 = 5;

Invalid signature type.

const EINVALID_SIGNATURE_TYPE: u64 = 2;

Account address mismatch.

const EACCOUNT_ADDRESS_MISMATCH: u64 = 7;

Invalid signature length.

const EINVALID_SIGNATURE_LENGTH: u64 = 4;

Invalid signing scheme type.

const EINVALID_SIGNING_SCHEME_TYPE: u64 = 3;

Function get_signing_scheme

Returns the signing scheme for the given value.

fun get_signing_scheme(value: u8): sui_derivable_account::SuiSigningScheme
Implementation
fun get_signing_scheme(value: u8): SuiSigningScheme {
    if (value == 0) SuiSigningScheme::ED25519
    else abort(EINVALID_SIGNING_SCHEME_TYPE)
}

Function deserialize_abstract_public_key

Deserializes the abstract public key which is supposed to be a bcs serialized SuiAbstractPublicKey.

fun deserialize_abstract_public_key(abstract_public_key: &vector<u8>): sui_derivable_account::SuiAbstractPublicKey
Implementation
fun deserialize_abstract_public_key(abstract_public_key: &vector<u8>): SuiAbstractPublicKey {
    let stream = bcs_stream::new(*abstract_public_key);
    let sui_account_address = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
    let domain = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
    assert!(!bcs_stream::has_remaining(&mut stream), EMALFORMED_DATA);
    SuiAbstractPublicKey { sui_account_address, domain }
}

Function deserialize_abstract_signature

Returns a tuple of the signature.

fun deserialize_abstract_signature(abstract_signature: &vector<u8>): sui_derivable_account::SuiAbstractSignature
Implementation
fun deserialize_abstract_signature(abstract_signature: &vector<u8>): SuiAbstractSignature {
    let stream = bcs_stream::new(*abstract_signature);
    let signature_type = bcs_stream::deserialize_u8(&mut stream);
    if (signature_type == 0x00) {
        let signature = bcs_stream::deserialize_vector<u8>(&mut stream, |x| deserialize_u8(x));
        assert!(!bcs_stream::has_remaining(&mut stream), EMALFORMED_DATA);
        SuiAbstractSignature::MessageV1 { signature }
    } else {
        abort(EINVALID_SIGNATURE_TYPE)
    }
}

Function split_signature_bytes

Splits raw signature bytes containing scheme flag (1 byte), signature (64 bytes) and public key (32 bytes) to a tuple of (signing_scheme, signature, public_key)

public fun split_signature_bytes(bytes: &vector<u8>): (u8, vector<u8>, vector<u8>)
Implementation
public fun split_signature_bytes(bytes: &vector<u8>): (u8, vector<u8>, vector<u8>) {
    // 1 + 64 + 32 = 97 bytes
    assert!(bytes.length() == 97, EINVALID_SIGNATURE_LENGTH);

    let signing_scheme = bytes[0];
    let abstract_signature_signature = vector::empty<u8>();
    let abstract_signature_public_key = vector::empty<u8>();

    // Extract signature (64 bytes)
    let i = 1;
    while (i < 65) {
        abstract_signature_signature.push_back(bytes[i]);
        i += 1;
    };

    // Extract public key (32 bytes)
    while (i < 97) {
        abstract_signature_public_key.push_back(bytes[i]);
        i += 1;
    };

    (signing_scheme, abstract_signature_signature, abstract_signature_public_key)
}

Function derive_account_address_from_public_key

Derives the account address from the public key and returns it is a hex string with “0x” prefix

fun derive_account_address_from_public_key(signing_scheme: u8, public_key_bytes: vector<u8>): vector<u8>
Implementation
fun derive_account_address_from_public_key(signing_scheme: u8, public_key_bytes: vector<u8>): vector<u8> {
    // Create a vector with signing scheme and public key bytes
    let data_to_hash = vector[signing_scheme];
    data_to_hash.append(public_key_bytes);

    // Compute blake2b hash
    let sui_account_address = aptos_hash::blake2b_256(data_to_hash);

    // Convert the address bytes to a hex string with "0x" prefix
    let sui_account_address_hex = b"0x";
    let i = 0;
    while (i < sui_account_address.length()) {
        let byte = sui_account_address[i];
        // Convert each byte to two hex characters
        let hex_chars = vector[
            if ((byte >> 4) < 10) ((byte >> 4) + 0x30) else ((byte >> 4) - 10 + 0x61),
            if ((byte & 0xf) < 10) ((byte & 0xf) + 0x30) else ((byte & 0xf) - 10 + 0x61)
        ];
        sui_account_address_hex.append(hex_chars);
        i += 1;
    };

    // Return the account address as hex string
    sui_account_address_hex
}

Function authenticate_auth_data

@deprecated This function is deprecated and will always abort.

public fun authenticate_auth_data(_aa_auth_data: auth_data::AbstractionAuthData, _entry_function_name: &vector<u8>)
Implementation
public fun authenticate_auth_data(
    _aa_auth_data: AbstractionAuthData,
    _entry_function_name: &vector<u8>
) {
    abort(EDEPRECATED)
}

Function authenticate_auth_data_internal

fun authenticate_auth_data_internal(aa_auth_data: auth_data::AbstractionAuthData, entry_function_name: &vector<u8>)
Implementation
fun authenticate_auth_data_internal(
    aa_auth_data: AbstractionAuthData,
    entry_function_name: &vector<u8>
) {
    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(&b"Sui", &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
    );
}

Function authenticate

Authorization function for domain account abstraction.

public fun authenticate(account: signer, aa_auth_data: auth_data::AbstractionAuthData): signer
Implementation
public fun authenticate(account: signer, aa_auth_data: AbstractionAuthData): signer {
    daa_authenticate(account, aa_auth_data, |auth_data, entry_name| authenticate_auth_data_internal(auth_data, entry_name))
}

Specification

Function derive_account_address_from_public_key

fun derive_account_address_from_public_key(signing_scheme: u8, public_key_bytes: vector<u8>): vector<u8>
pragma verify = false;

Function authenticate_auth_data

public fun authenticate_auth_data(_aa_auth_data: auth_data::AbstractionAuthData, _entry_function_name: &vector<u8>)
pragma verify = false;

Function authenticate_auth_data_internal

fun authenticate_auth_data_internal(aa_auth_data: auth_data::AbstractionAuthData, entry_function_name: &vector<u8>)
pragma verify = false;

Function authenticate

public fun authenticate(account: signer, aa_auth_data: auth_data::AbstractionAuthData): signer
pragma verify = false;