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
Conversation
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>
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. |
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.
The resolution for 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 |
Signed-off-by: Gaurav Narula <gnarula94@gmail.com>
Signed-off-by: Gaurav Narula <gnarula94@gmail.com>
I might still misunderstand what you're doing here. But from what I understand, this PR breaks one very big, important rule:
If you break this rule, various things will happen:
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 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 |
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:
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. |
Other comments for reviewers to be aware of: How to talk to Sovrin blockchaincalling 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 authproxAuthprox 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 { |
There was a problem hiding this comment.
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;
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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) |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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", |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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" |
There was a problem hiding this comment.
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
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") |
There was a problem hiding this comment.
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 |
There was a problem hiding this comment.
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)
Old issue is described in #2203 New idea, after discussion:
|
Closing this in light of the discussion and the new idea suggested above |
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 isassumed to be in
os.UserConfigDir()/indy/genesis