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

DARC: Add support for Sovrin DID Verification #2197

Closed
wants to merge 3 commits into from
Closed

Conversation

gnarula
Copy link
Contributor

@gnarula gnarula commented Feb 5, 2020

What this PR does

Adds support for Sovrin DID Resolution in Darcs using Indy CLI.
indy-cli is expected to be in $PATH. The pool configuration file is
assumed to be in os.UserConfigDir()/indy/genesis

DID Resolution depends on indy-cli which is expected
to be in $PATH. The pool configuration file is
assumed to be in `os.UserConfigDir()/indy/genesis`

Signed-off-by: Gaurav Narula <gnarula94@gmail.com>
@gnarula gnarula added this to WIP in Cothority via automation Feb 5, 2020
@gnarula gnarula moved this from WIP to Ready4Merge in Cothority Feb 5, 2020
@ineiti
Copy link
Member

ineiti commented Feb 5, 2020

Just to make sure: is it true that the darc-verification does not call out to the network.

Because it should always be possible to verify a signature even if there is no network. And old blocks might need to be replayed, so the signature verification should not depend on any services that are available only part-time.

@gnarula
Copy link
Contributor Author

gnarula commented Feb 5, 2020

Just to make sure: is it true that the darc-verification does not call out to the network.

At the moment it does require a network call to resolve a DID to a DID Document (aka determining the public keys associated with a DID). One future improvement can be to cache the DID document.

I should add that using a cached copy comes with the risk of the conodes using obsolete public keys to verify messages.

Because it should always be possible to verify a signature even if there is no network. And old blocks might need to be replayed, so the signature verification should not depend on any services that are available only part-time.

The resolution for did:sov method relies on the Sovrin blockchain to resolve the DID document and is highly available. Please refer to https://github.com/sovrin-foundation/indyscan/blob/master/daemon/genesis-data.js for an example list of validator nodes to connect to.

I think an edge case would be a user having rotated their keys and the blocks with old signatures being replayed. I recall some discussions about retreiving DIDDoc at timestamp=t which should definitely be possible since DID Docs have a history of changes like DARCs ;) This isn't supported in this PR at the moment because indy-cli doesn't expose that functionality.

Signed-off-by: Gaurav Narula <gnarula94@gmail.com>
Signed-off-by: Gaurav Narula <gnarula94@gmail.com>
@ineiti
Copy link
Member

ineiti commented Feb 6, 2020

I might still misunderstand what you're doing here. But from what I understand, this PR breaks one very big, important rule:

  • ClientTransactions must only depend on the global state and the information available in the block

If you break this rule, various things will happen:

  • replay is not possible anymore
  • some nodes will fail to confirm the ClientTransaction, while others might think it's OK
  • the evaluation of the ClientTransaction will be delayed because of network problems

This is the reason @jeffallen worked on the https://github.com/dedis/cothority/tree/master/authprox, to map external, centralised, network-accessed resources to something a ClientTransaction can trust.

I still might misunderstand what you're doing, but from what I understand now, you'll have to change this to be part of the authprox service!

@ineiti ineiti moved this from Ready4Merge to WIP in Cothority Feb 6, 2020
@jeffallen
Copy link
Contributor

Thanks for the feedback @ineiti . It is true that as I was helping Gaurav set his direction and plan this work I didn't take into consideration that ByzCoin needs to be self contained.

This work is fundamentally about adding a level of indirection between the Identity that signs a certain ByzCoin instruction and the public key that does the signing. The DID is the long-term name of a given identity, and DID resolution is the point where the DID is changed into the currently active public key. This code is currently structured close to right, in that it uses the DID as the signer's identity, and then does the resolution into a public key at the last possible moment before verifying.

The DID resolution step, in the case of Sovrin DID's, is handled by a permissioned blockchain. So what we are talking about is making a connection between two blockchains. The logical way to do that is for ByzCointo refer to a point in time (the block number) in the Sovrin chain.

So what I propose is to:

  • add an indication of which block number a certain DID should be resolved at to the Signer identity
  • in this PR or one coming soon to offer a second implementation of interface DIDResolver that can operate entirely in an offline manner, taking as input a genesis transaction (the trust root in the Sovrin world) and a set of blocks stored in a big file.

In this way, online nodes can resolve against the online Sovrin blockchain, and offline verifiers can still check that previous authentication decisions respected the DID holder's policy for which public key to use at the time when they sent in the tx.

@jeffallen
Copy link
Contributor

Other comments for reviewers to be aware of:

How to talk to Sovrin blockchain

calling out to indy-cli is a short-term hack to get us up and running, the focus of this PR needs to be on the flow of tx authorisation data from clients through darc authorisation, and into the ByzCoin ledger.

Why we are not using authprox

Authprox is for a situation where the transaction signer has a credential but is not able/willing to hold/protect a private key. So instead, the key is stored in a key escrow service for them, and is then used to sign messages on their behalf. In the case of authprox the key escrow is not centralised, but a collective authority that holds shares of a secret and then use their share to contribute a partial signature onto a statement like, "this user proved to me that IdP X recognises them".

In the sovereign identity use case, a ByzCoin transaction originator is holding on to their secret key, and ready to use it to sign. But they want to get the benefits of the existing key management infrastructure provided by other parts of the sovereign identity world.

if err != nil {
return nil
}
for _, pk := range doc.PublicKey {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a rather strange way of saying if len(doc.PublicKey) > 0 return doc.PublicKey[0] else return nil;

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gnarula : this for is a nice solution if you have a map. But as PublicKey is a slice, it's much simpler as jeff suggests.

@@ -987,6 +1034,11 @@ func (idp IdentityProxy) Equal(i2 *IdentityProxy) bool {
return idp.Data == i2.Data && idp.Public.Equal(i2.Public)
}

// Equal returns true if both IdentityDIDs are the same.
func (iddid IdentityDID) Equal(iddid2 *IdentityDID) bool {
return iddid.DID == iddid2.DID
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably the part after the # should be removed before comparing them. You will be able to use Go's URL parser for this, see https://play.golang.org/p/MZpRQCWJxec

// Verify returns nil if the signature is correct, or an error if something
// fails
func (iddid IdentityDID) Verify(msg, s []byte) error {
// TODO: Cache the results and set a TTL
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As discussed in the comments, this code will need to be adapted to resolve a given DID at a given point in time.

key := cothority.Suite.Point()
err := key.UnmarshalBinary(publicKey.Value)
if err != nil {
return fmt.Errorf("error unmarshalling key: %v", err)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a continue, so that a malformed key earlier in a DID Doc does not stop a correct key later in the DID Doc from working.

}
err = schnorr.Verify(cothority.Suite, key, msg, s)
if err == nil {
return nil
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Put a comment here so that old dumb guys like me recognise this is the happy path return point. Or re-write this so that the only return point is at the bottom of the function...


// overriding the global in the test
didResolver = &IndyCLIDIDResolver{
Path: "./mock_resolver_outputs/test_resolver",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of running a test_resolver from the unit test, teach IndyCLIDIDResolver that if it has an io.Reader set, then it should read from that instead of spawning indy-cli. Then you can have the canned results right here in the unit test.


patientDarc := NewDarc(patientRules, []byte("Patient Darc"))

signature := "4b61e1e81fb17145d9a560724f16902673a5b9c86cb5bd330d51722fcd66b9311b1821e319e9160062f88eab476a9e3bdc5e770f85b194eec91585fea24d400e"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Include instructions to the next poor sucker how to generate this, if needed.

"time"

"github.com/hyperledger/aries-framework-go/pkg/doc/did"
"github.com/mr-tron/base58"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please use the same base58 already used by aries-framework-go, so as to reduce the dependencies: github.com/btcsuite/btcutil/base58

See also: hyperledger-archives/aries-framework-go#1192

func (r *IndyCLIDIDResolver) Resolve(id string) (*did.Doc, error) {
output, err := r.executeCli(id)
if strings.Contains(output, "NYM not found") {
return nil, errors.New("DID not found in the ledger")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please follow the standard for use of xerror as proposed by @nkcr .

@@ -0,0 +1,45 @@
#!/bin/bash
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These files will go away, the content can be inside of the unit tests directly. (less files = more good, within reason)

@ineiti
Copy link
Member

ineiti commented Feb 7, 2020

Old issue is described in #2203

New idea, after discussion:

  • create a sovrin_pool contract where everybody can submit new pool proofs. The contract stores the latest proof of the pool-servers, together with the public keys of the pool-servers
  • every Instruction signature with DIDs must contain the resolved DID-object. instruction.VerifyWithOptions checks this signature using the sovrin-pool contract, and passes it to darc.EvalExpr if it matches.

@gnarula
Copy link
Contributor Author

gnarula commented Mar 5, 2020

Closing this in light of the discussion and the new idea suggested above

@gnarula gnarula closed this Mar 5, 2020
Cothority automation moved this from WIP to Closed Mar 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Cothority
  
Closed
Development

Successfully merging this pull request may close these issues.

None yet

3 participants