Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

would the community be willing to accept an AID export feature? #274

Closed
dhh1128 opened this issue Aug 30, 2024 · 16 comments
Closed

would the community be willing to accept an AID export feature? #274

dhh1128 opened this issue Aug 30, 2024 · 16 comments

Comments

@dhh1128
Copy link

dhh1128 commented Aug 30, 2024

I am interested in giving Signify an AID export feature (not a whole wallet export feature), where you could ask Signify to look up a particular single-sig AID's current priv+pub keypair and its next public key (the one it's committed to in prerotation), and give them back as raw key values.

Before you say it, I KNOW that giving back private keys is dangerous. It would obviously expose the secrets that are the basis of a given AID (except for the next private key) -- so if the person receiving these secrets manages them in a way that is less secure than Signify does, the trust in the AID could be lowered as a result. We have wallets for a reason...

I also know that if Signify is using keys from an HSM, what I've proposed is impossible; you can't get the keys back at all. You can only get a signature from it. So there would be cases where the feature wouldn't work.

So why do I want this? Well, because if I have the keys, I can build bridges to places that know nothing about KERI (yet), but that do understand Ed25519 keys. There are lots of places like that. For example, I can make an SSH key that I use to log in to servers or my github account. I can sign stuff with ssh-keygen. I can get an X509 with the public key from my AID. I can also make a did:key and did:peer (and other DID types too, I suppose) from the AID. And I can make other DID types from it, too. And all of those new places where I'm using my key are deriving their security from stuff that's still held in my Signify wallet, so I can think of the AID as being a root of trust for a whole family of derivative identifiers. The KERI ecosystem now has the option of telling people it has support for SSH, for X509, etc. I can also back up the key material in a password manager.

We could possibly reduce the risk of this feature by only exposing it when an AID is created or its keys are rotated, not when it is in a steady state. And we could expose it in a UI only after a consent screen that explains the risks.

If the community is willing to accept such a feature, I would be willing to invest the time to build it. But I don't want to work on it if it will be rejected for security reasons. So I thought I'd ask the question first. What do you think of the general idea?

Tagging @m00sey , @pfeairheller , @SmithSamuelM, @rodolfomiranda

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024 via email

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024 via email

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024 via email

@edeykholt
Copy link

Two questions come to mind, @dhh1128:

  1. What about a feature where a signify client provides a non-rotatable AID (essentially the public key corresponding to a private key it already has), then delegates to it? Would that feature enable many of your desired use cases?
  2. Without a AID private key export feature via Signify, how could a KERIA account owner move their AIDs controlled by one KERIA Service to another provider or platform? Could they request an export of their entire database, then import it to another KERIA instance, or decrypt its content with tools to extract key material needed? Without these types of features, is an AID essentially locked in with the KERIA provider?

@dhh1128
Copy link
Author

dhh1128 commented Aug 30, 2024

@SmithSamuelM said:

My first reaction is why not just delegate the other keys as the bridge.

This sounds cool, but I am not aware of a way to delegate to pure keys. AFAIK, the only way to delegate in KERI is cooperative delegation, which requires an AID on the receiving/delegatee side. Is there a way to inject an existing key pair (the pub part, I mean) into the routine that generates a delegated AID? If so, it could well address most of my intent, and I'd love to learn about it!

@edeykholt said:

What about a feature

Yes, this is pretty much what I need. Except I don't want a Signify client to provide it. I want to bring the pub key to KERI and have KERI delegate to it. But I don't know a way to do that.

how could a KERIA account owner move their AIDs... locked in with the KERIA provider?

I think we can write a feature that moves a wallet from one KERIA instance to another. But not a feature that moves individual AIDs.

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024

This functionality of moving keys around is not really part of KERI. It just so happens that keripy includes a key store and key store manager in the keri.app.keeping module.

The Manager class has a method called .replay and one called .ingest that will allow one to export i.e. replay the key history for a given AID and then import i.e ingest a key history created someplace else. Although replay only replays the public keys. to actually export the private keys requires writing a private key export function that uses the public key replay to look up and decrypt the private key and then export it. We didn't implement this because its generally a bad idea but given you do that the ingest method can import the private keys.

the Hab.make method already optionally allows one to ingest a key history into its associated key manager and then replay the key at the current key state so that one can then lookup and get the private keys to sign as needed..

There is a lot of functionality in the keeping module that very few know about because its not part of the KERI protocol but was created to enable basic wallet functionality in keripy.

@SmithSamuelM
Copy link

@dhh1128 The key manager keri.app.keeping.Manager can ingest private keys created elsewhere. So after the fact you can spin up a Manager and a Hab which Hab looks up the public key from the Manger so that the Hab tha can create the incept message that generates the AID for any set of private keys the Manager has already ingested. So you don't need to know the AID of the delegate until after the fact. So you can decide to delegate after the fact and create the delegated AID on the fly given you already have the key pairs to ingest. The point of the delegation is to enable some user to authentically attribute that the private keys that after the fact generated the delegated aid, were indeed blessed. So you can build the bridge afterwards you don't have to spin up a wallet to use the private keys. You can at any time spin up a wallet in the infrastructure that created the key pairs, ingest the key pairs, and then cooperatively delegate them via the resultant aid.

@SmithSamuelM
Copy link

So for example I can take my ledger nano create keys pairs use them to sign up etc and then later export the public keys to create an aid sign it with the ledger nano, publish it and then have it cooperatively delegated all after the fact of using those private keys via the nano. The nano never needs to know it is being used for KERI. So this is a bridge that backfills when an only when you need the backfill.

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024

The typical use case for the Keripy keeping.Manager is to NOT create and store private keys directly but to use a salt and an HKKey algorthm to generate the private keys on the fly from a public HDKey path. So moving private keys between infrastructures never happens. One moves the salt out-of-band and the public HKKey path in band. Moving the salt is easily accomplished by using something like 1password. So we really don't in general need to export anything other than the HDKey path, which actually for an existing KEL can be generated from the KEL itself (just the sn and rotation index of the public key history).

So if you are generating and storing private keys someplace not using an HDKey mechanism then you basically have private key material you can import after the fact. If on the other hadn your are using an HSM that doesn't allow you to export the salt then you can't import the private key material anyway so you have to always move the HSM from device to device. In either case, private key material never ever needs to be exported from a KERI wallet only imported to a KERI wallet.

The main reason not to use an salt with and hdkey algorithm inside a KERI wallet to generate private keys is because you believe that the an Hdkey algorithm is not secure enough. In that case you would never want to export the private keys because that defeats the whole point of not using an hdkey algorithm in the first place. The manager supports both, but in general NON hdkeys would be imported from some place else not created. And if they can be imported from someplace else then they already exist outside the KERI wallet and therefore don't need to be exported to move them to another wallet. If they exist outside, then they would most likely alreaddy be stored encrypted and so you have already had to solve the kick the can problem of the encryption keys independently so there is no benefit to resolving that problem just reuse the outside mechanism to reimport to multiple keri wallets.

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024

By the way, using KELs that contribute to a distributed multisig takes advantage of the fact that the key state of the contributing keys can be regenerated from the salt and HDKey path derived from the contributing KEL. So, the key manager doesn't need a different construct to support HDKeys for distributed multisig groups. If a given controller contributes keys from one of its own KELs then all the key manager has to track is its contributing KEL and the associated salt. It can regenerate the private key it needs to sign the group multisig messages from its contributed public key. and it can find where in its own contributed aid key history that contributed public key was used to get the HDpath if need be.
Since KELs are public all the ever needs to be moved between KERI wallets are salts and KELs. The salts OOB securely and the KELs are already public. Externally generated private key material is imported and reimported as need be never exported.

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024

So back to the original question: I assumed that if there is an application outside of KERI that needs private key material, then that application already has a way to generate that private key material. So KERI just needs to import it and bless it via delegation.

If that outside application does not already have a way to generate private key material then I strongly suggest that one use an HDKey generation algorithm with a secure Salt. The Salt can be created, stored, and moved between infrastructure using 1password, so no need to reinvent that wheel. So whats left to do. Implement an HDkey generation algorithm externally and track the HDKey path. If you want to use a KERI wallet for that, you can do that using the keeping.Manager with a non digest temporary AID that is just the fully qualified first public key. Ie. the aid is the first public key. You can move that key history to some other aid later using keeping.Manager.move

Now you can just track the keys and all you have is a placeholder aid. No KEL. At some time later you can create a real AID and KEL and assign (move) that key history in the the manager to that AID. It creates a forward index so one can track where it got moved to given the original placeholder aid.

Now you don't ever have to export private keys, just move the salt to some local application that can run the HDKey algorithm to regenerate the keys. This can just an application that is not KERI but just the keeping.Manager . The manager has a .sign method that lookups or regenerates the private keys and signs. This is how keriPy signs anything under the hood the Manager signs. So all you need is the keeping Module to implement a non-KERI wallet key generation storage and signing. The keeping.Manager HDKey path is designed such that its compatible with a KEL, so all you need is the Salt and a KEL and one can regenerate the path from the KEL. Or if you have the Manager then the path tracks a virtual KEL without needing a KEL because when you Manager.rotate it generates keys to match the HDKey algorthm that maps to the virtual KEL's key state that would have been created with that key rotation. You can then after the fact assign that key history to a real KEL and the AID is only generated after the fact (since the key history just uses a placeholder AID until you explicitly move it to some other aid. If its an ephermeral AID then you just need to manager.incept to create the key history path for the initial key and assign it to the placeholder AID. You can then move it to some other AID later. keeping.manager does not understand anything about KERI key events only key histories that follow a KEL compatible key generation algorthim.

keri.app.keeping is 1768 lines of python code including comments doc strings and white space. It imports several cesr (Matter subclasses) for seeds, salts, digests, etc. So not a big port. It has its own dedicated lmdb (not shared with the KEL lmdb). So it can be run as a standalone application that just manages key histories.

This functionality was by design and has been there for several years now.

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024

In the beginning was Key State and KERI was not. But the Key State was good so KERI was created to divide the Key State into verifiable events and it was good.

@SmithSamuelM
Copy link

SmithSamuelM commented Aug 30, 2024

One more thought. As I outlined above, exporting private keys means simple adding an export function to the keeping.Manager in keripy so its easy.

Whereas creating a standalone application to regenerate keys from the same salt, i.e. not exporting them. Is more work.

The risk here is that once people take the easy path of exporting private keys, the tendency (very real) is to create fragile weak key mangement infrastructure because exporting private keys makes them shared secrets not truly private keys anymore and then build applications that depend on such shared secret infrastructure. This is how we got into the shared secret insecurity that comes from attempting to manage a multiplicity of shared secrets that is endemic of the web now.

@SmithSamuelM
Copy link

What many don't appreciate it is that 1password follows best practices for managing secrets so one can use it to manage salts for HDKeys. 1password uses slow hashing 0.25 seconds so that any reasonable length password used to unlock 1password can't be brute forced in any reasonable amount of time. Likewise keripys keeping stretch algorithm uses slow hashing via the Argon2 algorithm that when set to the highest tier can take a long time to generate a key from a salt so guessing a salt to get a known key is not brute forceable.

So, basically, by not having any shared secrets, the attack vector is to get you to voluntarily unlock your 1 password or to intercept your salt when you copy it from 1password to unlock your Keri key store. But as soon as we enable exporting private keys, the attack vectors multiply without bounds.

@rodolfomiranda
Copy link
Collaborator

2. Without a AID private key export feature via Signify, how could a KERIA account owner move their AIDs controlled by one KERIA Service to another provider or platform? Could they request an export of their entire database, then import it to another KERIA instance, or decrypt its content with tools to extract key material needed? Without these types of features, is an AID essentially locked in with the KERIA provider?

KERIA doen't have the private keys. Only the Signify client has them unencrypted, and it can potentially use them with different KERIA providers

@dhh1128
Copy link
Author

dhh1128 commented Sep 10, 2024

We discussed on the community call on 10 Sep 2024, and agreed that we could achieve the goal without the suboptimal choice of exporting private keys. Fundamentally, we import the keys to KERI rather than creating them in KERI and exporting them.

@SmithSamuelM shared that in a habery's make() method, it is possible to pass a secrecies arg that allows the hab to be initialized with a pre-existing key. See roughly line 2190 and 2247 in the hab code (search for self.mgr.ingest). There are examples where this is used in tests/demos (see test_demos.py). What we would need to do is create an API that exposes this functionality so higher-level code could invoke it without direct access to the hab's make() method.

We also explored how to combine this with what Signify is doing. @pfeairheller pointed out that Signify already supports 3 AID creation methods (random, salt, HSM). We could add a 4th method that creates an AID from pre-existing keys, and then have Signify-TS just prompt for a privkey whenever it is needed.

I'm going to close this issue as resolved.

@dhh1128 dhh1128 closed this as completed Sep 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants