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

Update revocation demo #644

Merged
merged 11 commits into from Aug 5, 2020
32 changes: 11 additions & 21 deletions demo/AliceGetsAPhone.md
Expand Up @@ -21,19 +21,16 @@ This demo also introduces revocation of credentials.
- [Revoke the Credential and Send Another Proof Request](#revoke-the-credential-and-send-another-proof-request)
- [Conclusion](#conclusion)


## Getting Started

This demo can be run on your local machine or on Play with Docker (PWD), and will demonstrate credential exchange and proof exchange as well as revocation with a mobile agent. Both approaches (running locally and on PWD) will be described, for the most part the commands are the same, but there are a couple of different parameters you need to provide when starting up.
This demo can be run on your local machine or on Play with Docker (PWD), and will demonstrate credential exchange and proof exchange as well as revocation with a mobile agent. Both approaches (running locally and on PWD) will be described, for the most part the commands are the same, but there are a couple of different parameters you need to provide when starting up.

If you are not familiar with how revocation is currently implemented in Hyperledger Indy, [this article](https://github.com/hyperledger/indy-hipe/tree/master/text/0011-cred-revocation) provides a good background on the technique. A challenge with revocation as it is currently implemented in Hyperledger Indy is the need for the prover (the agent creating the proof) to download tails files associated with the credentials it holds.


### Get a mobile agent

Of course for this, you need to have a mobile agent. To find, install and setup a compatible mobile agent, follow the instructions [here](https://github.com/bcgov/identity-kit-poc/blob/master/docs/GettingApp.md).


### Running Locally in Docker

Open a new bash shell and in a project directory run the following:
Expand All @@ -47,7 +44,6 @@ We'll come back to this in a minute, when we start the `faber` agent!

There are a couple of extra steps you need to take to prepare to run the Faber agent locally:


#### Install ngrok and jq

[ngrok](https://ngrok.com/) is used to expose public endpoints for services running locally on your computer.
Expand All @@ -58,10 +54,9 @@ You can install ngrok from [here](https://ngrok.com/)

You can download jq releases [here](https://github.com/stedolan/jq/releases)


#### Expose services publicly using ngrok

Note that this is *only required when running docker on your local machine*. When you run on PWD a public endpoint for your agent is exposed automatically.
Note that this is _only required when running docker on your local machine_. When you run on PWD a public endpoint for your agent is exposed automatically.

Since the mobile agent will need some way to communicate with the agent running on your local machine in docker, we will need to create a publicly accesible url for some services on your machine. The easiest way to do this is with [ngrok](https://ngrok.com/). Once ngrok is installed, create a tunnel to your local machine:

Expand All @@ -78,13 +73,12 @@ Forwarding http://abc123.ngrok.io -> http://localhost:8020
Forwarding https://abc123.ngrok.io -> http://localhost:8020
```

This creates a public url for ports 8020 on your local machine.
This creates a public url for ports 8020 on your local machine.

Note that an ngrok process is created automatically for your tails server.

Keep this process running as we'll come back to it in a moment.


### Running in Play With Docker

To run the necessary terminal sessions in your browser, go to the Docker playground service [Play with Docker](https://labs.play-with-docker.com/). Don't know about Play with Docker? Check [this out](https://github.com/cloudcompass/ToIPLabs/blob/master/docs/LFS173x/RunningLabs.md#running-on-play-with-docker) to learn more.
Expand All @@ -98,7 +92,6 @@ cd aries-cloudagent-python/demo

We'll come back to this in a minute, when we start the `faber` agent!


### Run an instance of indy-tails-server

For revocation to function, we need another component running that is used to store what are called tails files.
Expand All @@ -111,38 +104,37 @@ Open a new bash shell, and in a project directory, run:
git clone https://github.com/bcgov/indy-tails-server.git
cd indy-tails-server/docker
./manage build
GENESIS_URL=http://test.bcovrin.vonx.io/genesis ./manage start
./manage start
```

This will run the required components for the tails server to function and make a tails server available on port 6543.

This will also automatically start an ngrok server that will expose a public url for your tails server - this is required to support mobile agents. The docker output will look something like this:
This will also automatically start an ngrok server that will expose a public url for your tails server - this is required to support mobile agents. The docker output will look something like this:

```bash
ngrok-tails-server_1 | t=2020-05-13T22:51:14+0000 lvl=info msg="started tunnel" obj=tunnels name="command_line (http)" addr=http://tails-server:6543 url=http://c5789aa0.ngrok.io
ngrok-tails-server_1 | t=2020-05-13T22:51:14+0000 lvl=info msg="started tunnel" obj=tunnels name=command_line addr=http://tails-server:6543 url=https://c5789aa0.ngrok.io
```

Note the server name in the `url=https://c5789aa0.ngrok.io` parameter (`https://c5789aa0.ngrok.io`) - this is the external url for your tails server. Make sure you use the `https` url!

Note the server name in the `url=https://c5789aa0.ngrok.io` parameter (`https://c5789aa0.ngrok.io`) - this is the external url for your tails server. Make sure you use the `https` url!

### Run `faber` With Extra Parameters

If you are running in a *local bash shell*, navigate to [The demo direcory](/demo) and run:
If you are running in a _local bash shell_, navigate to [The demo direcory](/demo) and run:

```bash
TAILS_NETWORK=docker_tails-server LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo faber --revocation --events
```

The `TAILS_NETWORK` parameter lets the demo script know how to connect to the tails server (which should be running in a separate shell on the same machine).

If you are running in *Play with Docker*, navigate to [The demo direcory](/demo) and run:
If you are running in _Play with Docker_, navigate to [The demo direcory](/demo) and run:

```bash
PUBLIC_TAILS_URL=https://def456.ngrok.io LEDGER_URL=http://test.bcovrin.vonx.io ./run_demo faber --revocation --events
```

The `PUBLIC_TAILS_URL` parameter lets the demo script know how to connect to the tails server. This can be running in another PWD session, or even on your local machine - the ngrok endpoint is public and will map to the correct location.
The `PUBLIC_TAILS_URL` parameter lets the demo script know how to connect to the tails server. This can be running in another PWD session, or even on your local machine - the ngrok endpoint is public and will map to the correct location.
nrempel marked this conversation as resolved.
Show resolved Hide resolved

Note that you _must_ use the `https` url for the tails server endpoint.

Expand All @@ -162,10 +154,9 @@ As part of its startup process, the agent will publish a revocation registry to
<img src="./collateral/revocation-2-ledger.png" alt="Ledger">
</details>


## Accept the Invitation

When the Faber agent starts up it automatically creates an invitation and generates a QR code on the screen. On your mobile app, select "SCAN CODE" (or equivalent) and point your camera at the generated QR code. The mobile agent should automatically capture the code and ask you to confirm the connection. Confirm it.
When the Faber agent starts up it automatically creates an invitation and generates a QR code on the screen. On your mobile app, select "SCAN CODE" (or equivalent) and point your camera at the generated QR code. The mobile agent should automatically capture the code and ask you to confirm the connection. Confirm it.

<details>
<summary>Click here to view screenshot</summary>
Expand All @@ -185,7 +176,7 @@ The mobile agent will give you feedback on the connection process, something lik

Switch your browser back to Play with Docker. You should see that the connection has been established, and there is a prompt for what actions you want to take, e.g. "Issue Credential", "Send Proof Request" and so on.

Tip: If your screen is too small to display the QR code (this can happen in Play With Docker because the shell is only given a small portion of the browser) you can copy the invitation url to a site like https://www.the-qrcode-generator.com/ to convert the invitation url into a QR code that you can scan. Make sure you select the `URL` option, and copy the `invitation_url`, which will look something like:
Tip: If your screen is too small to display the QR code (this can happen in Play With Docker because the shell is only given a small portion of the browser) you can copy the invitation url to a site like https://www.the-qrcode-generator.com/ to convert the invitation url into a QR code that you can scan. Make sure you select the `URL` option, and copy the `invitation_url`, which will look something like:

```bash
https://abfde260.ngrok.io?c_i=eyJAdHlwZSI6ICJkaWQ6c292OkJ6Q2JzTlloTXJqSGlxWkRUVUFTSGc7c3BlYy9jb25uZWN0aW9ucy8xLjAvaW52aXRhdGlvbiIsICJAaWQiOiAiZjI2ZjA2YTItNWU1Mi00YTA5LWEwMDctOTNkODBiZTYyNGJlIiwgInJlY2lwaWVudEtleXMiOiBbIjlQRFE2alNXMWZwZkM5UllRWGhCc3ZBaVJrQmVKRlVhVmI0QnRQSFdWbTFXIl0sICJsYWJlbCI6ICJGYWJlci5BZ2VudCIsICJzZXJ2aWNlRW5kcG9pbnQiOiAiaHR0cHM6Ly9hYmZkZTI2MC5uZ3Jvay5pbyJ9
Expand All @@ -199,7 +190,6 @@ http://ip10-0-121-4-bquqo816b480a4bfn3kg-8020.direct.play-with-von.vonx.io?c_i=e

Note that this will use the ngrok endpoint if you are running locally, or your PWD endpoint if you are running on PWD.


## Issue a Credential

We will use the Faber console to issue a credential. This could be done using the Swagger API as we have done in the connection process. We'll leave that as an exercise to the user.
Expand Down
24 changes: 14 additions & 10 deletions demo/ngrok-wait.sh
Expand Up @@ -6,17 +6,21 @@
if ! [ -z "$TAILS_NGROK_NAME" ]; then
echo "ngrok tails service name [$TAILS_NGROK_NAME]"
NGROK_ENDPOINT=null
while [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]
do
echo "Fetching endpoint from ngrok service"
NGROK_ENDPOINT=$(curl --silent $TAILS_NGROK_NAME:4040/api/tunnels | ./jq -r '.tunnels[0].public_url')

if [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]; then
echo "ngrok not ready, sleeping 5 seconds...."
sleep 5
fi
done
JQ=${JQ:-`which jq`}
if [ -x "$JQ" ]; then
while [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]
do
echo "Fetching endpoint from ngrok service"
NGROK_ENDPOINT=$(curl --silent $TAILS_NGROK_NAME:4040/api/tunnels | $JQ -r '.tunnels[0].public_url')

if [ -z "$NGROK_ENDPOINT" ] || [ "$NGROK_ENDPOINT" = "null" ]; then
echo "ngrok not ready, sleeping 5 seconds...."
sleep 5
fi
done
else
echo " not found"
fi
export PUBLIC_TAILS_URL=$NGROK_ENDPOINT
echo "Fetched ngrok tails server endpoint [$PUBLIC_TAILS_URL]"
fi
Expand Down
29 changes: 7 additions & 22 deletions demo/runners/faber.py
Expand Up @@ -227,11 +227,7 @@ async def main(
" (3) Send Message\n"
)
if revocation:
options += (
" (4) Revoke Credential\n"
" (5) Publish Revocations\n"
" (6) Add Revocation Registry\n"
)
options += " (4) Revoke Credential\n" " (5) Publish Revocations\n"
options += " (T) Toggle tracing on credential/proof exchange\n"
options += " (X) Exit?\n[1/2/3/{}T/X] ".format(
"4/5/6/" if revocation else ""
Expand Down Expand Up @@ -321,18 +317,7 @@ async def main(
for req_pred in req_preds
},
}
# test with an attribute group with attribute value restrictions
# indy_proof_request["requested_attributes"] = {
# "n_group_attrs": {
# "names": ["name", "degree", "timestamp", "date"],
# "restrictions": [
# {
# "issuer_did": agent.did,
# "attr::name::value": "Alice Smith"
# }
# ]
# }
# }

if revocation:
indy_proof_request["non_revoked"] = {"to": int(time.time())}
proof_request_web_request = {
Expand Down Expand Up @@ -379,11 +364,6 @@ async def main(
)
except ClientError:
pass
elif option == "6" and revocation:
log_status("#19 Add another revocation registry")
await agent.create_and_publish_revocation_registry(
credential_definition_id, TAILS_FILE_COUNT
)

if show_timing:
timing = await agent.fetch_timing()
Expand Down Expand Up @@ -465,6 +445,11 @@ async def main(

require_indy()

if args.revocation and not args.tails_server_base_url:
raise Exception(
"If revocation is enabled, --tails-server-base-url must be provided"
)

try:
asyncio.get_event_loop().run_until_complete(
main(
Expand Down
72 changes: 5 additions & 67 deletions demo/runners/support/agent.py
Expand Up @@ -6,8 +6,6 @@
import os
import random
import subprocess
import hashlib
import base58
from timeit import default_timer

from aiohttp import (
Expand Down Expand Up @@ -38,6 +36,8 @@

AGENT_ENDPOINT = os.getenv("AGENT_ENDPOINT")

PUBLIC_TAILS_URL = os.getenv("PUBLIC_TAILS_URL")
nrempel marked this conversation as resolved.
Show resolved Hide resolved

DEFAULT_POSTGRES = bool(os.getenv("POSTGRES"))
DEFAULT_INTERNAL_HOST = "127.0.0.1"
DEFAULT_EXTERNAL_HOST = "localhost"
Expand Down Expand Up @@ -137,6 +137,9 @@ def __init__(
self.trace_target = TRACE_TARGET
self.trace_tag = TRACE_TAG

if PUBLIC_TAILS_URL:
self.tails_server_base_url = PUBLIC_TAILS_URL

self.admin_url = f"http://{self.internal_host}:{admin_port}"
if AGENT_ENDPOINT:
self.endpoint = AGENT_ENDPOINT
Expand Down Expand Up @@ -210,71 +213,6 @@ async def register_schema_and_creddef(
log_msg("Cred def ID:", credential_definition_id)
return schema_id, credential_definition_id

async def create_and_publish_revocation_registry(
self, credential_def_id, max_cred_num
):
revoc_response = await self.admin_POST(
"/revocation/create-registry",
{
"credential_definition_id": credential_def_id,
"max_cred_num": max_cred_num,
},
)
revocation_registry_id = revoc_response["result"]["revoc_reg_id"]
tails_hash = revoc_response["result"]["tails_hash"]

# get the tails file from "GET /revocation/registry/{id}/tails-file"
tails_file = await self.admin_GET_FILE(
f"/revocation/registry/{revocation_registry_id}/tails-file"
)
hasher = hashlib.sha256()
hasher.update(tails_file)
my_tails_hash = base58.b58encode(hasher.digest()).decode("utf-8")
log_msg(f"Revocation Registry ID: {revocation_registry_id}")
assert tails_hash == my_tails_hash

tails_file_url = (
f"{self.public_tails_url}/revocation/registry/"
f"{revocation_registry_id}/tails-file"
)
if os.getenv("PUBLIC_TAILS_URL"):
tails_file_url = f"{self.public_tails_url}/{revocation_registry_id}"
tails_file_external_url = (
f"{self.public_tails_url}/{revocation_registry_id}"
)
elif RUN_MODE == "pwd":
tails_file_external_url = f"http://{self.external_host}".replace(
"{PORT}", str(self.admin_port)
)
else:
tails_file_external_url = f"http://127.0.0.1:{self.admin_port}"
tails_file_external_url += (
f"/revocation/registry/{revocation_registry_id}/tails-file"
)

revoc_updated_response = await self.admin_PATCH(
f"/revocation/registry/{revocation_registry_id}",
{"tails_public_uri": tails_file_url},
)
tails_public_uri = revoc_updated_response["result"]["tails_public_uri"]
assert tails_public_uri == tails_file_url

revoc_publish_response = await self.admin_POST(
f"/revocation/registry/{revocation_registry_id}/publish"
)

# if PUBLIC_TAILS_URL is specified, upload tails file to tails server
if os.getenv("PUBLIC_TAILS_URL"):
tails_server_hash = await self.admin_PUT_FILE(
{"genesis": await default_genesis_txns(), "tails": tails_file},
tails_file_url,
params=None,
)
assert my_tails_hash == tails_server_hash.decode("utf-8")
log_msg(f"Public tails file URL: {tails_file_url}")

return revoc_publish_response["result"]["revoc_reg_id"]

def get_agent_args(self):
result = [
("--endpoint", self.endpoint),
Expand Down