Module 0x1::sui_derivable_account
Derivable account abstraction that verifies a message signed by Sui wallet.
- The message format is as follows:
Please confirm you explicitly initiated this request from
Nonce:
- The abstract public key is a BCS serialized
SuiAbstractPublicKey. - The abstract signature is a BCS serialized
SuiAbstractSignature. - This module has been tested for the following wallets:
-
Slush
-
Phantom
-
Nightly
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
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
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;