EIP ERC App Keys: application specific wallet accounts

ETHcc video where I introduce this work.

3 Likes

hey @Bunjin, great proposal! We’ve been thinking about something similar to this in Status for a long time, getting the derivation path from ENS names, and I like this solution! we would love to contribute to this and implement it when it’s finalized.
I also think this can work on top of eip-1581, what do you think?
I saw this in the proposal this:

EIP 1581: Non-wallet usage of keys derived from BIP-32 trees also discussed here proposes a scheme that relies on a list of indexes where application should register (similar to SLIP0044 list for instance).

But EIP-1581 only defines a standard way to derive non-wallet keys, without requiring apps to be registered. Only the key_type should be, so in this case the key type can be apps / dapps, and the the sub paths what you already proposed, which I like especially in the way we can get the derivation path from a ENS name.

So if we have both we can have general non-wallet keys not based on app/domains, and apps keys based on specific apps/dapps.

What do you think?

2 Likes

Hi @gravityblast, thanks a lot for your comment and suggestion!

I think this EIP 1775 moves away from EIP1581 for several reasons:

EIP1581 proposes to use a path starting by

m / purpose' / coin_type' / subpurpose' / key_type' / key_index

m / 43' / 60' / 1581'

First of all BIP 43’ is not a purpose field since it’s the global standard for the purpose field structure. BIP44 is BIP 43 compliant. If we deviate from BIP44, we need a different purpose field than 43 and 44 altogether.
Second, it uses coin type 60, which I think is not necessary here. We already have the BIP44 structure of main accounts for transacting Ether, here we are trying to define applications keys, which are not Ethereum specific, so I don’t think we need a coin type field there.

Third EIP 1581 would like to introduce a key type field which poses several questions imho.

Why separate the keys by what this EIP calls type?

The EIP says for this field:

Describes the purpose for which the key is being used.

What key types does this EIP propose? Where are they referenced, how is this list maintained? But a key is a key, there are no key types by definition, only potentially by use. We don’t need impose to split the keys by purpose, or at least we don’t need to impose that. What would be the benefit of this field?

More importantly EIP1581 is silent about authentication. How does one request access to a given key (key type and key index)? How does one prove he is using the key type required?

What’s preventing you to use EIP1775 for “non wallet usage of keys”? An application authenticated through its name and then gets access to the keys for its domain (we are not retricted to DNS or ENS names).
If you’d like to introduce a general application such as “ssh” at the wallet level, then we can use EIP1775 to derive keys for this domain’s name and skip authentication.

So all things considered I think the EIP we propose here is more global than EIP1581 and that it solves several issues of EIP1581.

Why would you like to preserve EIP1581, what do you think that EIP would allow that EIP1775 would not?

thank you @Bunjin! I’m happy to both use 1775 or 1581, if we find that they have many things in common it might make sense!

We started with a single m/EIP_NUMBER too, but then @Arachnid helped us using the right path according to BIP43:

Purpose code 43 is reserved for usage by non-Bitcoin cryptocoins. In this case, the level immediately below the purpose code is hardened derivation using the index for the cryptocoin specified by SLIP-0044. The details of levels below this coin type are to be entirely decided by the standard process of the specific cryptocoin, but it is recommended that the very next level be a sub-purpose code with a similar use as this BIP’s purpose code.
So that the recommended use for non-Bitcoin cryptocoins is:
m / 43' / coin_type' / subpurpose' / ...

that’s why we ended up using that path.

In our case we were thinking about 2 different native chat apps, where a user can recover an account from the mnemonic and in both apps use the same keys, so different apps where the only common thing is the “purpose” of the key (chat, storage, etc…). I see the difference since in the other case it’s a wallet/dapp-browser that manages the keys for the current app.

Yeah you are completely right! A key is a key, so it can be used for anything. But in our case we wanted to have a standard based on the purpose so that if I’m chatting with you, I can change to a different native app and still using the same key if the other client uses the right “chat” key. Same like BIP44, I can change wallet but I have the same keys based on the coin type.

Yeah that’s not covered because it’s not intended to be chosen by a dapp, but by the wallet itself. I see your point though, that’s why I thought 1775 can add those points on top of 1581.

I’m still thinking about a native app. What would you use instead of the dapp domain or name so that the same key can be shared in different apps?

Let’s see if we can find some common points, or maybe they are just 2 completely different use cases and it’s fine too :slight_smile:

3 Likes

Thanks for your detailed answer @gravityblast. Now I think understand better what you are aiming at and how our efforts relate.

A] First of all a few comments about your EIP1581 and what you mentionned about your choice of an HD Path:

  1. HD path BIP44:

HD Path BIP32 compliant
BIP 43 only suggests that the first field should be a purpose field,

m / purpose' / *

Then there is BIP 44 that specifies the whole path as

m / purpose' / coin_type' / account' / change / address_index

and SLIP44

This uses a purpose field of 44
and not 43 as mentioned in your link by arachnid and a PR (not merged by LukeJr).

See my references and also the Ethereum EIPs that specifies that we should use

m/44'/60'/a'/0/n'

and slip44 list : https://github.com/satoshilabs/slips/blob/master/slip-0044.md

So in any case if you are willing to use BIP44, I think you should use purpose field 44’ and not 43’, no matter the coin type.

  1. HD path BIP43 deviating from BIP44:

When deviating from BIP44, one should use a different purpose field than 44’ but it has not been standardized to use 43’ and cointype’.

In fact several other indexes have been already used and standardized see:

and what I mention in the EIP, where I reference all these:

Importantly, I don’t see why these standards should be related to a coin type and I would refuse to restrict to what luke JR suggested (that wasn’t merged) as :

m / 43' / coin_type' / subpurpose' /

Imho that’s too restrictive to subject these to a coin_type.

B] Now about your use case and how EIP1775 could allow for it:

I understand and agree that it’s an important use case to be able to implement non transacting, cross apps/domains keys (for instance for chat or others).

For that I see two alternatives:

  1. develop an EIP alongside 1775, such as yours
    but then I’d recommend to use a different purpose field than 43 and 44, and not used already also I think we should remove the coin type since apps such as chat are not necessarly related to a coin.

  2. [I favor this approach for the sake of simplicity] Use EIP 1775 to encompass all in the following way:
    For cross applications non transacting keys (not main accounts), we need a different naming scheme than DNS or ENS and we assume that the wallet or the clients will not have to authenticate these but will include them in their core logic.

This naming scheme would be something like cak:[name]
where cak (cross app key is just a header that allows to fully distinguish these from DNS/ENS) and name is the standardized name of the use case.

e.g.: cak:whisper, cak:ssh, cak:swarm

we also need to specify the hashing scheme for these in a way that ensures no collision with the current hashing in EIP1775 of ENS/DNS names. It should be fairly easy.

With this 2nd approach, this would mean 3 types of keys:

  • main accounts (along BIP44 eth standard, and potentially for other cryptocurencies too)
  • EIP1775 app specific keys authenticated through ENS and/or DNS
  • EIP1775 cross apps/domains keys not authenticated but included in the wallet/client.

See ongoing discussion here (comments by @Arachnid ) about the BIP43 purpose field:

There is no standardised prefix for coins other than Bitcoin besides BIP44 - which is organised around UTXO coins and one specific use-case. Given the Bitcoin community’s resistance to expending effort on helping other projects with standardisation, it’s unlikely we’ll ever have one - so the unmerged proposal from luke-jr is the closest we’re likely to come.

It’s definitely in contravention of BIP44 to use m/44'/60'/ as a prefix and not use the remaining fields as specified.

I’m not sure why you think this is a restriction, or unacceptable. Can you clarify?

When there’s multiple independent teams trying to use a namespace without collisions, we need a way to divide it up to ensure that collisions don’t happen. Using this proposal is one way to do that, and allows us to define any subtree we want without any chance of overlap.

using hashes generates very long paths, which is a performance issue for resource-limited wallets like the Keycard or the Ledger (there can even be limits on the allowed nesting in some wallets). This is the reason why we went with a key usage specifier instead in EIP-1581 for the general case, still allowing a hashing scheme like the one defined in EIP-1775 to be implemented in the key_index field

1 Like

Of course, I’ll try to clarify. Imho the EIP1775 or also EIP1581 are transversal to all cryptocurrencies and even more global than that because one could like to use chat keys or application specific keys without using any cryptocurrency. Thus EIP1775 or EIP1581 should in fact be global Crypto IPs.
Therefore any standard we propose here should not be a subset of a coin_type. I know that it’s only something that’s in the HD path, but I think it would matter and for instance that would mean that if other cryptocurrencies teams would like to use app keys or non wallet keys, then they would use a path that includes Ethereum coin_type which is kind of weird and would lead to confusion imho.

I fully agree about the need to avoid collision in a proper way. What you suggest is one way indeed (and it relies on the IPs list to be fully separated within each “cryptocurrency space”), however like I mentioned above it imposes a separation per crypto currency (and these to be subjected to cryptocurrencies).
What we propose here is another approach to avoid collision: using a purpose field list, which in any case will be required at the BIP43 level since several purpose fields are already in use (and some of them are not BIP related).

In any case, I’m open to solving this in another way, similar to what you subject but philosophically (and not only) I’m against imposing the use of a coin_type in the path for the reasons mentioned above.

Good point, and yes you are right, the depth of the HD path matters. We are currently trying to get feedback from HW dev to understand their limitations with respect to this.

However it seems the computational complexity is linear with the depth of the HD tree.

Also there are other ways to reduce the depth of the EIP1775 path, such as only using 128bits Uids.

In any case, that’s a good point and I’ll report here the results of discussions with HW devs.

Edit: Just talked to Ledger CTO we will do a benchmark (time / depth) analysis and report the findings.

As I said - the presence of the coin type field is only namespacing. It doesn’t limit the use of the standard, it’s simply where it’s defined. You’re defining something in the EIP process; the only part of the namespace you can issue yourself is the part (nominally) controlled by that process.

Analagously, suppose you’re producing a product for sale worldwide, and you need a UPC (barcode) number. Because you are in the US, your UPC codes will be prefixed with that because that is where the product originated - but that says nothing about where the product can be sold.

The root of the HD key namespace is controlled by BIP43, which says that the first field will be a BIP number. If you want to use any part of the namespace other than the one we’ve managed to get somewhat ad-hoc allocated to non-Bitcoin projects, you’ll need to write a BIP and get it approved. The EIP process can’t help with that.

1 Like

Thanks a lot for your answer @Arachnid !

Great analogy, however for this proposal, I think we would be more in a situation where we are defining a new way of defining bar codes (fully compatible with the previous technology), defining a core standard for wallets, not labeling a product.

For the sake of pragmatism, I’m ok with using a path like you suggest (m/43’/60’/1775’) but there may be an advantage of using a child depth of 1 (like I initially suggested) for the purpose field is that it’s obviously less depth than 3 and thus less computationally heavy (see comments by @bitgamma above).

Another option may be indeed to submit part of this as a BIP to claim a first depth BIP43 purpose field.

Since the prefix is fixed it doesn’t add much to computation time, it can be easily computed once, cached and used as the derivation root for this kind of key (of course, this requires changes to the firmware). What is more of concern is the dynamic part of the key, especially when using hashes or random UIDs, since it changes completely from app to app without any common prefix.

Hi,

It took me a while but I finally had to time to think a bit more about the proposal and I am bit concerned about its practicalities.
The idea of isolating apps as much as this proposal does is in my opinion not necessarily a good thing.

Of course this is use case dependent but I think users would benefit more in isolating a subset of apps, like games vs defi vs social platforms, instead of apps themselves.

After all one of the main benefit of blockchain is interoperability and while such proposal does not block interoperability it makes it harder for let say 2 games to easily let players use their items in both.

The reason why, is that the proposal hide away the persona account.
I’d like to propose a change so that app keys would only act as delegate signing keys on behalf of the persona account. In a way that smart contract can validate that such delegate has indeed right to act on behalf of the persona for that particular action.

This means that such appkeys would never perform any transaction and should not hold any fund.

This is obviously a considerable change to the idea but this is what I am looking for as an app developer and that is why I proposed a similar mechanism via changes to EIP-712 so that the account remain a identifier shared across app.
Appkeys would be able to integrating it at a lower level though, supporting all signature scheme.

Also, as mentioned in my previous comment I really think the proposal should add a mechanism to use content-addressable network URI as application names. This should actually be the preferred method since it ensure the application code cannot change (assuming it is not linking to other URI, in which case the browser can show a warning).

ENS names and DNS names on the other hand should not be considered for app keys without a warning to the user. After all ENS and DNS are vulnerable to the name owner changing the underlying application.

For ENS, if the name resolve to a content-addressable URI as mentioned above, then the application name should be based on the URI and in that case no warning would be necessary.

Hi,

Sorry I wasn’t able to answer before to your important comments.

Regarding the concern about not necessarily isolating apps, EIP 1775 also mentions “cross app keys” which can use a different naming scheme than ENS or DNS.
Their names (to be standardized and referenced in some lists) would be “cak:name” (e.g. “cak:dex”, or “cak:gambling” …) and we would derive an uid in the same way as from ens names. (see here: EIP ERC App Keys: application specific wallet accounts)

This would allow several and independant apps to use the same set of accounts (if the user authorizes the visited website to have access to her dex or gambling keys for instance) while still allowing each app to have access to isolated and app specific accounts using the ENS / DNS naming.

Agreed about the importance of warning users about ENS / DNS names vulnerabilities, however this is already somewhat the case when you visit a website and trust it to generate transactions on your behalf.

Ok, I missed the part about “cross app keys”. Maybe the proposal should clarify how these are chosen. Is it the application itself who propose the key to use or is the idea that wallet will ask the user upfront into which “category” the app should fit ?

Actually this should be the latter as the user is best placed to decide how it organise its applications

Agreed about the importance of warning users about ENS / DNS names vulnerabilities, however this is already somewhat the case when you visit a website and trust it to generate transactions on your behalf.

My point is that it is not the case for content-addressable uri like ipfs. Like a smart contract, such website could be audited or even formaly verified to never do anything without a confirmation popup for example. Then since their content can’t change without a uri change, you are guaranteed that a key derived from it will different.

That’s why they should be the first case supported by such proposal. ENS and DNS should be seen as convenience only.

So if these redirect to a content-addressable uri, such uri should be used for key derivation, instead of the domain name itself.

This way if the domain name change to a new uri, the key is a different one. The one associated with the original uri is thus protected.

1 Like

At MetaMask, we have begun to experiment internally with a variation of this app keys proposal. Its implementation is a bit simpler:

const appKeyPrivKey = ethUtil.keccak(privKey + originString, 256)

This could be exposed as an RPC method to allow any domain to request its own app key associated with the current requested account (if available):

const appKey = await provider.send({
  method: 'wallet_getAppKeyForAccount',
  params: [address1]
});

An important thing to define here will be how we generate originString, since this needs to be completely unique to the requesting domain.

We may be able to simply include the protocol prefix domain separator like : to separate a unique type string from the main domain identifier. For example, a few valid origin strings might be:

https://ethereum-magicians.org
ens://danfinlay.eth
eth://0x5A0b54D5dc17e0AadC383d2db43B0a0D3E029c4c
ipfs://QmVhFMu7ryuFyRXm8gpxWmVuSsLR5H9Z91j8YcTrc9GmUL

This allows each page to have its own per-account deterministic seed.

While this does not explicit cover cases of sharing these app keys between pages on its own, I believe that need can be met by composition:

Since a domain would get a unique key, and because domains can intercommunicate, one domain (app) could request another domain (signer) to perform its cryptographic operation on some data, with its appKey as a seed, potentially allowing new signing strategies to be added as easily as new websites.

This could also pass it to domains that are loading specific signing strategies. This may sound dangerous at first, but if a domain represents a static hash of a trusted cryptographic function implementation, it could be as safe as calling any audited internal dependency.

Anyways, posting it here because we’re experimenting with this variant, and I wanted to get it out there in case anyone else is about to implement the parent EIP, which I may revise or submit an alternative to soon.

3 Likes

I haven’t read through everything here but I wanted to drop a quick comment:

I’m not a fan of privacy theater. People can create multiple accounts all they want, but until there are better mechanisms for laundering assets between accounts anything that tries to separate accounts is just privacy theater. We have seen this in Bitcoin that even with the UTXO model and people actively taking steps to remain anonymous they still get pinpointed via chain analysis.

With Ethereum, I feel like it is made much more clear to the user “your transactions are all linkable to each other”, rather than giving them a false sense of privacy where privacy doesn’t exist. If people really want to disassociate their various transactions, they have to go through a lot of effort and while I do think that we should try to enable/simplify that (e.g., tornado.cash) that, we don’t yet have the primitives to achieve true privacy on Ethereum.

1 Like

This proposal is not motivated mostly by privacy, but also has implications for supporting additional key types, and other cryptography, where an app requires access to a unique secret.

1 Like

We updated EIP1775 to a skinnier and simpler version, using what Dan suggested in one of his last posts in this thread:

const appKeyPrivKey = ethUtil.keccak(privKey + originString, 256)

See here:

1 Like