Skip to content

Using nns-dapp and quill with the same seed will generate different principal_id #863

Open
@YellowOpenSource

Description

@YellowOpenSource

The principal_id generated locally through the seed is inconsistent with the principal_id displayed on the nns dapp:

Take the following two pieces of code as an example:

seed:1947400 story goddess birth execute canoe sense poverty orchard feel mule indoor credit bulb arrest primary giggle turtle order tell glimpse grid act motion february

nns generated: jwfy3-6d6bo-ri5le-apblk-7tmrd-h7sj5-srhi4-4wntx-4daz6-missz-yqe
local generated: nrw4v-2qe5h-dor7z-mm7x4-hfa5f-tfls6-avaja-aysww-c6bwg-g25mq-jqe

local code
import { Ed25519KeyIdentity } from '@dfinity/identity';
import { mnemonicToSeedSync } from "bip39";
import {
    pub_key_to_address,
} from '@dfinity/rosetta-client';
let crypto;

const HARDENED = 0x80000000;

function toBigEndianArray(n) {
    const byteArray = new Uint8Array([0, 0, 0, 0]);
    for (let i = byteArray.length - 1; i >= 0; i--) {
        const byte = n & 0xff;
        byteArray[i] = byte;
        n = (n - byte) / 256;
    }
    return byteArray;
}

async function derive(
    parentKey,
    parentChaincode,
    i,
) {
    // From the spec: Data = 0x00 || ser256(kpar) || ser32(i)
    const data = new Uint8Array([0, ...parentKey, ...toBigEndianArray(i)]);
    const key = await crypto.subtle.importKey(
        'raw',
        parentChaincode,
        {
            name: 'HMAC',
            hash: { name: 'SHA-512' },
        },
        false,
        ['sign'],
    );

    const h = await crypto.subtle.sign('HMAC', key, data.buffer);
    const slipSeed = new Uint8Array(h.slice(0, 32));
    const chainCode = new Uint8Array(h.slice(32));
    return [slipSeed, chainCode];
}

async function generateMasterKey(
    seed,
) {
    const data = new TextEncoder().encode('ed25519 seed');
    const key = await crypto.subtle.importKey(
        'raw',
        data,
        {
            name: 'HMAC',
            hash: { name: 'SHA-512' },
        },
        false,
        ['sign'],
    );
    const h = await crypto.subtle.sign('HMAC', key, seed);
    const slipSeed = new Uint8Array(h.slice(0, 32));
    const chainCode = new Uint8Array(h.slice(32));
    return [slipSeed, chainCode];
}

export async function fromSeedWithSlip0010(
    masterSeed,
    derivationPath,
) {
    let [slipSeed, chainCode] = await generateMasterKey(masterSeed);

    for (let i = 0; i < derivationPath.length; i++) {
        [slipSeed, chainCode] = await derive(
            slipSeed,
            chainCode,
            derivationPath[i] | HARDENED,
        );
    }

    return Ed25519KeyIdentity.generate(slipSeed);
}

async function main() {
    try {
        crypto = await import('node:crypto');
    } catch (err) {
        console.log('crypto support is disabled!');
    }
    crypto.subtle
    const mem = "story goddess birth execute canoe sense poverty orchard feel mule indoor credit bulb arrest primary giggle turtle order tell glimpse grid act motion february"
    const seed = mnemonicToSeedSync(mem);
    const id = await fromSeedWithSlip0010(seed, [44, 223, 0, 0, 0]);
    console.log(id.getPublicKey().toDer())
    // console.log(pub_key_to_address(id.getPublicKey()))
}

main()
nns-dapp code
import { Ed25519KeyIdentity } from '@dfinity/identity';
import { mnemonicToSeedSync } from "bip39";
let crypto;

const HARDENED = 0x80000000;

function toBigEndianArray(n) {
    const byteArray = new Uint8Array([0, 0, 0, 0]);
    for (let i = byteArray.length - 1; i >= 0; i--) {
        const byte = n & 0xff;
        byteArray[i] = byte;
        n = (n - byte) / 256;
    }
    return byteArray;
}

async function derive(
    parentKey,
    parentChaincode,
    i,
) {
    // From the spec: Data = 0x00 || ser256(kpar) || ser32(i)
    const data = new Uint8Array([0, ...parentKey, ...toBigEndianArray(i)]);
    const key = await crypto.subtle.importKey(
        'raw',
        parentChaincode,
        {
            name: 'HMAC',
            hash: { name: 'SHA-512' },
        },
        false,
        ['sign'],
    );

    const h = await crypto.subtle.sign('HMAC', key, data.buffer);
    const slipSeed = new Uint8Array(h.slice(0, 32));
    const chainCode = new Uint8Array(h.slice(32));
    return [slipSeed, chainCode];
}

async function generateMasterKey(
    seed,
) {
    const data = new TextEncoder().encode('ed25519 seed');
    const key = await crypto.subtle.importKey(
        'raw',
        data,
        {
            name: 'HMAC',
            hash: { name: 'SHA-512' },
        },
        false,
        ['sign'],
    );
    const h = await crypto.subtle.sign('HMAC', key, seed);
    const slipSeed = new Uint8Array(h.slice(0, 32));
    const chainCode = new Uint8Array(h.slice(32));
    return [slipSeed, chainCode];
}

export async function fromSeedWithSlip0010(
    masterSeed,
    derivationPath,
) {
    let [slipSeed, chainCode] = await generateMasterKey(masterSeed);

    for (let i = 0; i < derivationPath.length; i++) {
        [slipSeed, chainCode] = await derive(
            slipSeed,
            chainCode,
            derivationPath[i] | HARDENED,
        );
    }

    return Ed25519KeyIdentity.generate(slipSeed);
}

async function main() {
    try {
        crypto = await import('node:crypto');
    } catch (err) {
        console.log('crypto support is disabled!');
    }
    crypto.subtle
    const mem = "story goddess birth execute canoe sense poverty orchard feel mule indoor credit bulb arrest primary giggle turtle order tell glimpse grid act motion february"
    const seed = mnemonicToSeedSync(mem);
    const id = await fromSeedWithSlip0010(seed, [44, 223, 0, 0, 0]);
    console.log(id.getPrincipal().toString())
}

main()

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions