nucypher/docs/source/guides/development/getting_started.rst

321 lines
9.4 KiB
ReStructuredText

Getting Started with Characters
===============================
* `A Note about Side Channels`_
* `Running an Ethereum Node`_
* `Connecting to The NuCypher Network`_
* `Alice: Grant Access to a Secret`_
* `Setup Alice`_
* `Grant`_
* `Enrico: Encrypt a Secret`_
* `Setup Enrico`_
* `Encrypt`_
* `Bob: Decrypt a Secret`_
* `Setup Bob`_
* `Join a Policy`_
* `Retrieve and Decrypt`_
A Note about Side Channels
--------------------------
The NuCypher network does not store or handle an application's data; instead - it manages access *to* application data.
Management of encrypted secrets and public keys tends to be highly domain-specific - The surrounding architecture
will vary greatly depending on the throughput, sensitivity, and sharing cadence of application secrets.
In all cases, NuCypher must be integrated with a storage and transport layer in order to function properly.
Along with the transport of ciphertexts, a nucypher application also needs to include channels for Alice and Bob
to discover each other's public keys, and provide policy encrypting information to Bob and Enrico.
Side Channel Application Data
-----------------------------
* Secrets:
* Message Kits - Encrypted Messages, or "Ciphertexts"
* Identities:
* Alice Verifying Key - Public key used for verifying Alice
* Bob Encrypting Key - Public key used to encrypt for Bob
* Bob Verifying Key - Public key used to verify Bob
* Policies:
* Policy Encrypting Key - Public key used to encrypt messages for a Policy.
* Labels - A label for specifying a Policy's target, like a filepath
Running an Ethereum Node
------------------------
Operation of a decentralized NuCypher character [\ ``Alice``\ , ``Bob``\ , ``Ursula``\ ] requires
a connection to an Ethereum node and wallet to interact with smart
contracts (https://docs.nucypher.com/en/latest/architecture/contracts.html).
For general background information about choosing a node technology and operation,
see https://web3py.readthedocs.io/en/stable/node.html.
In this guide, a local Geth node connected to the Goerli Testnet is used.
For detailed information on using the geth CLI and Javascript console,
see https://geth.ethereum.org/interface/Command-Line-Options.
To run a Goerli-connected Geth node in *fast* syncing mode:
.. code-block:: bash
$ geth --goerli
To run a Goerli-connected Geth node in *light* syncing mode:
.. code-block:: bash
$ geth --goerli --syncmode light
Note that using ``--syncmode light`` is not 100% stable but can be a life saver when using
a mobile connection (or congested hackathon wifi...).
Connect to the Geth Console to test your ethereum node's IPC:
.. code-block:: bash
$ geth attach ~/.ethereum/goerli/geth.ipc
Wallets
^^^^^^^
To list available accounts on your geth node (Hardware wallet addresses will also be listed here
if one is attached to the system hardware):
.. code-block:: bash
$ geth attach ~/.ethereum/goerli/geth.ipc
> eth.accounts
["0x287a817426dd1ae78ea23e9918e2273b6733a43d"]
To create a new software based Geth account:
.. code-block:: bash
$ geth attach ~/.ethereum/goerli/geth.ipc
> personal.newAccount()
...
"0xc080708026a3a280894365efd51bb64521c45147"
Note that the Geth console does not return EIP-55 compliant checksum addresses, and instead will output
the *lowercase* version of the address. Since Nucypher requires EIP-55 checksum addresses, you will need
to convert your address to checksum format:
.. code-block:: javascript
> web3.toChecksumAddress(eth.accounts[0])
"0x287A817426DD1AE78ea23e9918e2273b6733a43D"
Connecting to The NuCypher Network
----------------------------------
Provider URI
^^^^^^^^^^^^
Nucypher uses the ethereum node's IPC-File to communicate, specified by ``provider_uri``.
By default in ubuntu, the path is ``~/.ethereum/goerli/geth.ipc`` - This path
will also be logged to the geth-running console on startup.
Connecting Nucypher to an Ethereum Provider
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
.. code-block:: python
from nucypher.blockchain.eth.interfaces import BlockchainInterfaceFactory
BlockchainInterfaceFactory.initialize_interface(provider_uri='~/.ethereum/goerli/geth.ipc')
Ursula: Untrusted Re-Encryption Proxies
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
When initializing an ``Alice``\ , ``Bob``\ , or ``Ursula``\ , an initial "Stranger-\ ``Ursula``\ " is needed to perform
the role of a ``Teacher``\ , or "seednode":
.. code-block:: python
from nucypher.characters.lawful import Ursula
seed_uri = "gemini.nucypher.network:9151"
seed_uri2 = "104.248.215.144:9151"
ursula = Ursula.from_seed_and_stake_info(seed_uri=seed_uri)
another_ursula = Ursula.from_seed_and_stake_info(seed_uri=seed_uri2)
Stranger ``Ursula``\ s can be created by invoking the ``from_seed_and_stake_info`` method, then a ``list`` of ``known_nodes``
can be passed into any ``Character``\ 's init. The ``known_nodes`` will inform your character of all of the nodes
they know about network-wide, then kick-off the automated node-discovery loop:
.. code-block:: python
from nucypher.characters.lawful import Alice
alice = Alice(known_nodes=[ursula, another_ursula], ...)
For information on how to run a staking Ursula node via CLI,
see `Running a Network Node </guides/network_node/network_node>`_.
Alice: Grant Access to a Secret
-------------------------------
Setup Alice
^^^^^^^^^^^
Create a NuCypher Keyring
.. code-block:: python
from nucypher.config import NucypherKeyring
keyring = NucypherKeyring.generate(checksum_address='0x287A817426DD1AE78ea23e9918e2273b6733a43D', password=PASSWORD)
.. code-block:: python
from nucypher.characters.lawful import Alice, Ursula
ursula = Ursula.from_seed_and_stake_info(seed_uri='gemini.nucypher.network:9151')
# Unlock Alice's Keyring
keyring = NucypherKeyring(account='0x287A817426DD1AE78ea23e9918e2273b6733a43D')
keyring.unlock(password=PASSWORD)
# Instantiate Alice
alice = Alice(keyring=keyring, known_nodes=[ursula], provider_uri='~/.ethereum/goerli/geth.ipc')
# Start Node Discovery
alice.start_learning_loop(now=True)
Alice needs to know about Bob in order to grant access by acquiring Bob's public key's through
the application side channel:
.. code-block:: python
from umbral.keys import UmbralPublicKey
verifying_key = UmbralPublicKey.from_hex(verifying_key),
encrypting_key = UmbralPublicKey.from_hex(encryption_key)
Grant
^^^^^
Then, Alice can grant access to Bob:
.. code-block:: python
from nucypher.characters.lawful import Bob
from datetime import timedelta
import maya
bob = Bob.from_public_keys(verifying_key=bob_verifying_key, encrypting_key=bob_encrypting_key)
policy_end_datetime = maya.now() + timedelta(days=5) # Five days from now
policy = alice.grant(bob,
label=b'my-secret-stuff', # Sent to Bob via side channel
m=2, n=3,
expiration=policy_end_datetime)
policy_encrypting_key = policy.public_key
Enrico: Encrypt a Secret
------------------------
Setup Enrico
^^^^^^^^^^^^
First, a ``policy_encrypting_key`` must be retrieved from the application side channel, then
to encrypt a secret using Enrico:
Encrypt
^^^^^^^
.. code-block:: python
from nucypher.characters.lawful import Enrico
enrico = Enrico(policy_encrypting_key=policy_encrypting_key)
ciphertext, signature = enrico.encrypt_message(message=b'Peace at dawn.')
The ciphertext can then be sent to Bob via the application side channel.
Note that Alice can get the public key even before creating the policy.
From this moment on, any Data Source (Enrico) that knows the public key
can encrypt data originally intended for Alice, but can be shared with
any Bob that Alice grants access.
``policy_pubkey = alice.get_policy_encrypting_key_from_label(label)``
Bob: Decrypt a Secret
---------------------
For Bob to retrieve a secret, The ciphertext, label, policy encrypting key, and Alice's verifying key must all
be fetched from the application side channel. Then, Bob constructs his perspective of the policy's network actors:
Setup Bob
^^^^^^^^^
.. code-block:: python
from nucypher.characters.lawful import Alice, Bob, Enrico, Ursula
# Application Side-Channel
# --------------------------
# label = <Side Channel>
# ciphertext = <Side Channel>
# policy_encrypting_key = <Side Channel>
# alice_verifying_key = <Side Channel>
# Everyone!
ursula = Ursula.from_seed_and_stake_info(seed_uri='gemini.nucypher.network:9151')
alice = Alice.from_public_keys(verifying_key=alice_verifying_key)
enrico = Enrico(policy_encrypting_key=policy_encrypting_key)
# Generate and unlock Bob's keyring
keyring = NucypherKeyring.generate(checksum_address='0xC080708026a3A280894365Efd51Bb64521c45147', password=PASSWORD)
keyring.unlock(PASSWORD)
# Make Bob
bob = Bob(known_nodes=[ursula], checksum_address="0xC080708026a3A280894365Efd51Bb64521c45147")
Join a Policy
^^^^^^^^^^^^^
Next, Bob needs to join the policy:
.. code-block:: python
bob.join_policy(label=label, alice_verifying_key=alice.public_keys(SigningPower), block=True)
Retrieve and Decrypt
^^^^^^^^^^^^^^^^^^^^
Then Bob can retrieve, and decrypt the ciphertext:
.. code-block:: python
cleartexts = bob.retrieve(label=label,
message_kit=ciphertext,
data_source=enrico,
alice_verifying_key=alice.public_keys(SigningPower))