Can you get the private key of a public key generated by tECDSA?

I understand that the canister does not hold the private key of a public key. However, let’s say that I no longer want to use this “wallet” in my canister and want to port it over to a wallet UI such as Metamask. Could I do this?

Nevermind here. It looks like each canister can only hold one key. Why is that? Are there plans to allow a canister to hold multiple public keys?

No, you can not get the private key generated by t-ecdsa, because it uses a distributed key generation technology to generate the private key, and the private key is distributed all over the ICP nodes, so no one can get them.

2 Likes

A canister can hold arbitrarily many keys. The canister is free to specify any BIP-32 style derivation path to derive as many keys as it wants.

It is not possible to export any of the private keys. The reason is that they are related through the derivation mechanism, hence not entirely independent. If you exported one you could potentially export all.

1 Like

Thank you so much for the responses, @timo and @cymqqqq. This makes a lot of sense. Is there anyone building a UI that I could in theory connect my canister with a public key to?

I thought that ecdsa_public_key call to the management canister only generates one public key per canister. Are you saying that in theory a canister can hold as many keys but in practice, no?

The call argument to ecdsa_public_key contains the derivation_path. A derivation path is just a vector (up to length 255) of byte strings (or blobs). Each derivation path corresponds to a unique public key. You can also see that the call argument also has an optional canister_id. The canister_id gets prepended to the derivation path. If you look at the sign_with_ecdsa argument you notice that theres no canister_id` property which makes sense. Each canister can ask for the ECDSA public keys of other canisters, but a canister can only request signatures for the ECDSA keys corresponding to the respective canister.

  ecdsa_public_key : (record {
    canister_id : opt canister_id;
    derivation_path : vec blob;
    key_id : record { curve: ecdsa_curve; name: text };

  sign_with_ecdsa : (record {
    message_hash : blob;
    derivation_path : vec blob;
    key_id : record { curve: ecdsa_curve; name: text };
  }) -> (record { signature : blob });

On another note, the management canister doesn’t store the public keys that have been requested, but they are always computed using the canister id and derivation path.

1 Like

Understood.

I must have thought that only one key could exist for each canister because I was passing in the same derivation_path.

I tried passing a different derivation_path and got a different public key.

Since the management canister does not have the public keys that have been requested:

  1. What is the best way to keep track of all of the public keys that a canister has created? Store it in a mutable data structure?
  2. What is the best way to create unique derivation paths? I can easily generate unique strings to pass in as a derivation path but is there a preferred, secure way?
  3. What is the best way to store the derivation paths that you can create?

I’d go for the mutable data structure myself

It depends on your use case. In many apps the user principal converted to text would be the default strategy. A really cool feature is that you don’t need to care about keeping derivation paths secret. Only your canister can access the key if it’s run by the right subnet. And in the background the canister id is added to the derivation path, so no two canisters can create the same derived key

That depends entirely on your use case. The most common pattern is to use some id that you associated with the thing that requests encryption. For example the user principal, a chat room id, or the index in your item list

1 Like