KeystoneHQ / Keystone-developer-hub

Keystone developer hub
19 stars 7 forks source link

Aptos account address generation and authentication key rotation #6

Open zhaomengru2015 opened 1 year ago

zhaomengru2015 commented 1 year ago

Background

In the Aptos blockchain, when creating a new account, a 32-byte authentication key will be created first, this authentication key will be the account's address. The authentication key will change when generating a new pair of the private and public keys to rotate the authentication key, but the account address will not change.

Motivation

Currently, there is no standard regarding the address generation and authentication key rotation implementations, wallets are using different approaches, leading to several problems:

  1. If wallets are using different address generation solutions, Accounts generated in one wallet might not be able to import to another wallet, which is not a best practice for the web3 world.
  2. It's hard for the wallet software to manage multiple accounts with one mnemonic word.
  3. Wallets are using their own authentication key rotation solutions, making accounts hard to recover. Or wallets might not even implement the key rotation functionality, increasing the asset loss risks if the authentication key is compromised.

Current status

Address generation

Currently different wallets are using different ways for address generation:

  1. Petra wallet generate the public / private key pair follow the BIP-44 standard, using this private key to initialize the Aptos Account, below is the code snippet to verify the wallet address generated in Petra wallet.
import * as bip39 from 'bip39';
import {HDKey} from "@scure/bip32";

const generateAccount = ()=>{
    const seed = bip39.mnemonicToSeedSync(code);
    const node = HDKey.fromMasterSeed(Buffer.from(seed));
    const exKey = node.derive(metaData.derivationPath);
    return new AptosAccount(exKey.privateKey);   
}
  1. Fewcha wallet uses the mnemonic seed to initialize the Aptos account, below is the code snippet to verify the address generated in Fewcha wallet.
import * as bip39 from 'bip39';
const WORDS="rich guitar rally exercise radio food wish pluck input broccoli sample wing";

const generateAccount = () => {
    const seed = bip39.mnemonicToSeedSync(WORDS);
    return new AptosAccount(seed);
}
  1. Martian wallet follows the SLIP-10 standard to initialize the Aptos account, below is the code snippet to verify the wallet address generated in Martian wallet.
import * as bip39 from 'bip39';
import {derivePath} from "ed25519-hd-key";
const WORDS="rich guitar rally exercise radio food wish pluck input broccoli sample wing";
const path = "m/44'/637'/0'/0'/0'";

export const getAptosAccount = () => {
    const seed = bip39.mnemonicToSeedSync(process.env.WORDS)
    const { key } = derivePath(path, seed.toString('hex'));
    return new AptosAccount(new Uint8Array(key));
}

Authentication key rotation

After a great discussion about authentication key rotation, the OriginatingAddress technology has been implement.

Proposal

Account generation

For the account generation, we propose the SLIP-10 for several benefits:

  1. It's easy and straightforward, most mainstream wallets on the market(like MetaMasl, Tezor, Ledger, Keystone, etc) are following SLIP-10.
  2. It's easy for multiple account management, wallets can manage multiple accounts with one mnemonic words, by increasing the "BIP-44 account index" for generating addresses.

Authentication key rotation

For the authentication key rotation, we propose to follow the OriginatingAddress implementation, a brief description of this implementation can be found here, new mnemonic words rotation should be used for security.

Here is the detailed way of Account generation and Key rotation.

Account generation

  1. derive private and public key pair for ed25519 curve for hdPath "m/44'/637'/0'/0'/0'" following the slip10 standard. the sha3-256(pubkey_A | signing_schema) will be the account's initial authentication key according to the Aptos Authentication Key Definition, so as the account's address.
  2. more private and public key pairs can be derived by increasing the account level hdPath described in step 1; respect the address gap limit described below.
image

Address gap limit

Address gap limit should be set. if the wallet hit this limit, it expects there are no used addresses beyond this point and stop searching the address chain.

Code snippet

Address generation

import {derivePath} from "ed25519-hd-key";
import * as bip39 from 'bip39';
import {AptosAccount} from "aptos";
import fetch from "cross-fetch";

// For demo, never use it to manage your assets.
const WORDS="rich guitar rally exercise radio food wish pluck input broccoli sample wing";
const getAccountFromMetaData = (code,metaData)=> {
    const seed = bip39.mnemonicToSeedSync(code.toString());
    const { key } = derivePath(metaData.derivationPath, seed.toString('hex'));
    return new AptosAccount(key, metaData.address);
}

const createNewAccounts = async (code) => {
    const accounts = [];
    for (let i = 0; i < ACCOUNT_GAP_LIMIT; i += 1) {
        const derivationPath = `m/44'/637'/${i}'/0'/0'`;
        const acc = getAccountFromMetaData(code,{
            derivationPath
        });
        const address = acc.authKey().toString();
        const response = await fetch(
            `${NODE_URL}/accounts/${address}`,
            {
                method: "GET",
            }
        );
        if (response.status === 404) {
            accounts.push({
                derivationPath,
                address,
                publicKey: acc.pubKey().toString(),
            });
        }
    }
    return accounts;
}
createNewAccounts(WORDS);

Authentication key rotation

  1. Wallet use mnemonic phrase to generate an Ed25519 private/public key pair (priv_a, pub_a) follow SLIP-10, with hdPath "m/44'/637'/{accountIndex}'/0'/0'", account_a created with authKey auth_key = sha_256(pub_a | scheme_id).
  2. Wallet a new mnemonic phrase to generate an Ed25519 private/public key pair (priv_b, pub_b) follow SLIP-10, with the same accountIndex from step 1, account_b is created on-chain, with the auth_key=sha_256(pub_b | schema_id).
  3. Rotate the authentication key follow this technology.
zhaomengru2015 commented 1 year ago

I think the user case for key rotation is more like a best security practice, like today organizations rotate their credentials periodically. For the case "the mnemonic words exported", I think rotate mnemonic words might not solve the problem. If user's mnemonic words was exported, it much likely that the assets has already lost. So in this case, the first thing user should do is transfer the assets to another safer place, not rotate another authentication key with a new mnemonic word.

MartianSiddharth commented 1 year ago

I think mnemonic phrase rotation would be more powerful compared to key rotation but I understand the complexity it brings to hardware wallets. Key rotation can be used as a best practice too but providing both these features to users is a big UX problem.

MartianSiddharth commented 1 year ago

Based on the discussion from this thread, Martian wallet will be using a different key rotation technique and the mnemonic phrase rotation will leverage this algorithm. Aptos Wallet is also using this technique for key rotation.

MartianSiddharth commented 1 year ago

I agree with your proposal to incorporate SLIP-10 to use ed25519 curve instead of secp256k1 curve but why are we not using SLIP-10 for master key generation. Can you please elaborate on this part?

generate the master seed from mnemonic words following the bip39 standard. derive private and public key pair for ed25519 curve for hdPath "m/44'/637'/0'/0'/0'" following the slip10 standard. the sha3-256(pubkey_A | signing_schema) will be the account's initial authentication key according to the Aptos Authentication Key Definition, so as the account's address. more private and public key pairs can be derived by increasing the account level hdPath described in step 2; respect the address gap limit described below.

zhaomengru2015 commented 1 year ago

@MartianSiddharth about the mnemonic phrase rotation, can you please give more details about the algorithm?

zhaomengru2015 commented 1 year ago

@MartianSiddharth as for the SLIP-10 master key generation, my fault, what I mean was definitely using SLIP10 to master key generation, and derive child private and public key pairs for account and rotation keys, let me update the issue description.

MartianSiddharth commented 1 year ago

@zhaomengru2015 I had another doubt about SLIP-10.

For the ed25519 curve the private keys are no longer multipliers for the group generator; instead the hash of the private key is the multiplier. For this reason, our scheme for ed25519 does not support public key derivation and uses the produced hashes directly as private keys.

Does this mean that we will not be able to derive public keys?

zhaomengru2015 commented 1 year ago

@MartianSiddharth No,We don't. We can only get privatekey from slip10. Aptos will generate an ed25519 signing key pair with nacl

zhaomengru2015 commented 1 year ago

@MartianSiddharth , can we support key rotation for hardware wallet? like when users connect Keystone in Martian, then using key rotation, users select a derivationPath and Keystone generate the key. Using mnemonic rotation if users connect without hardware wallet?

I think mnemonic phrase rotation would be more powerful compared to key rotation but I understand the complexity it brings to hardware wallets. Key rotation can be used as a best practice too but providing both these features to users is a big UX problem.

zhaomengru2015 commented 1 year ago

@MartianSiddharth do you have any feedback about the Key rotation suggestions ⬆️ ? Or you still want to go with only mnemonic rotation at this stage, I think that could be fine.

aaronisme commented 1 year ago

move this to the official AIP repo https://github.com/aptos-labs/aip/issues/2