Zondax / ledger-oasis

Oasis app for Ledger Nano S and X
Apache License 2.0
3 stars 6 forks source link

Question : How to use HD ED25519 keys generated by ledger outside of it ? #84

Closed Esya closed 3 years ago

Esya commented 3 years ago

Hi,

I'm currently considering developing a web wallet for Oasis that would have BIP-39/44 support and offer a variety of tools to follow their tokens/handle staking/delegation, but I'd like to support mnemonic phrases and their derived keys as provided by ledger-oasis or bitpie

I've seen that it's more frequent to use secp256k1 as an eliptic curve, is there any specific reason to use ed25519 instead ? It seems bitpie is also doing the same but currently by using those mnemonics (ledger or bitpie) even using something like https://github.com/alepop/ed25519-hd-key I'm not falling back to the right pub/private key pairs by using the standard derivation path m/44'/474'/0'/0/0

Would you have any pointers here ? I'd like to offer full ledger support at some point if I do move forward with this project but not being able for users to use their existing wallet & forcing them to generate new keys would be a big downside.

Thanks in advance

tjanez commented 3 years ago

Here is a modification of Ledger's Orakolo Python Implementation of BIP32-Ed25519 that replicates the Oasis' Ledger app:

   ... trimmed ...

    o = BIP32Ed25519()
    # Zondax ledger-oasis development mnemonic.
    # https://github.com/Zondax/ledger-oasis/blob/8ba79334d9fc025776738416fc9494894bba4d4b/deps/ledger-zxlib/dockerized_build.mk#L139.
    mnemonic = 'equip will roof matter pink blind book anxiety banner elbow sun young'
    for path in ("44'/474'/5'/0'/3'",): #
        print("*************************************")
        print("CHAIN: %s"%path)
        print("private derivation")
        node = o.derive_mnemonic(True, path, mnemonic)
        ((kL, kR), A, c) = node
        print("  kL:%s" % binascii.hexlify(kL))
        print("  kR:%s" % binascii.hexlify(kR))
        print("   c:%s" % binascii.hexlify(c))
        print("   A:%s" % binascii.hexlify(A))

        # NOTE: The Oasis Ledger app doesn't use BIP32-Ed25519's private and
        # public key directly but uses the obtained kL (first 32 bytes) of the
        # 64 byte BIP32-Ed25519 derived private key as Ed25519's seed (i.e.
        # non-extended private key).

        # Initialize Ed25519 key from seed.
        I = bytearray(_h512(kL))
        kL, kR = I[:32], I[32:]
        # the lowest 3 bits of the first byte of kL of are cleared
        kL[0]  = _clear_bit( kL[0], 0b00000111)
        # the highest bit of the last byte is cleared
        kL[31] = _clear_bit(kL[31], 0b10000000)
        # the second highest bit of the last byte is set
        kL[31] =   _set_bit(kL[31], 0b01000000)

        cv25519 = Curve.get_curve("Ed25519")
        k_scalar = int.from_bytes(bytes(kL), 'little')
        P = k_scalar*cv25519.generator
        A =  cv25519.encode_point(P)

        print("\nPublic key:\n")
        print(binascii.hexlify(A))

        print("\n Oasis account address:\n")
        subprocess.run(shlex.split("oasis-node stake pubkey2address --public_key {}".format(base64.b64encode(A).decode())), check=True)
        print()

This is the output one should get:

... trimmed ...

Leave derive_seed
  kL:b'58438fbaae6d0192420b1793a80b5579ae5ca30cbe51e4746cee81c4944b1257'
  kR:b'70dfacd37c00539d51fc0cdf7f4f457b0efa46241f69cb249d2837ecc5508c65'
   c:b'94aaa7974cad3d44b04d8f17ff06920251ebe2a9eac0ecfa6e86d29e0063c91d'
   A:b'8c1335620aa82c5b0a7995a4127390e902bcbb7b1852419b0b7c093b29760201'

Public key:
b'aba52c0dcb80c2fe96ed4c3741af40c573a0500c0d73acda22795c37cb0f1739'

Oasis account address:
oasis1qphdkldpttpsj2j3l9sde9h26cwpfwqwwuhvruyu
jleni commented 3 years ago

Specification have changed, so this will not be applicable in future