# Using pyUmbral¶

## Elliptic Curves¶

The matter of which curve to use is the subject of some debate. If you aren’t sure, you might start here: https://safecurves.cr.yp.to/

A number of curves are available in the Cryptography.io library, on which pyUmbral depends.
You can find them in the `cryptography.hazmat.primitives.asymmetric.ec`

module.

Important

Be careful when choosing a curve - the security of your application depends on it.

We provide curve `SECP256K1`

as a default because it is the basis for a number of crypto-blockchain projects;
we don’t otherwise endorse its security.
We additionally support curves `SECP256R1`

(also known as “NIST P-256”) and `SECP384R1`

(“NIST P-384”), but they cannot currently be selected via the public API.

## Encryption¶

### Generate an Umbral key pair¶

First, let’s generate two asymmetric key pairs for Alice: A delegating key pair and a signing key pair.

```
>>> from umbral import SecretKey, Signer
>>> alices_secret_key = SecretKey.random()
>>> alices_public_key = alices_secret_key.public_key()
>>> alices_signing_key = SecretKey.random()
>>> alices_verifying_key = alices_signing_key.public_key()
>>> alices_signer = Signer(alices_signing_key)
```

### Encrypt with a public key¶

Now let’s encrypt data with Alice’s public key.
Invocation of `umbral.encrypt()`

returns both a `capsule`

and a `ciphertext`

.
Note that anyone with Alice’s public key can perform this operation.

```
>>> from umbral import encrypt
>>> plaintext = b'Proxy Re-encryption is cool!'
>>> capsule, ciphertext = encrypt(alices_public_key, plaintext)
```

### Decrypt with a private key¶

Since data was encrypted with Alice’s public key, Alice can open the capsule and decrypt the ciphertext with her private key.

```
>>> from umbral import decrypt_original
>>> cleartext = decrypt_original(alices_secret_key, capsule, ciphertext)
```

## Threshold Re-Encryption¶

### Bob Exists¶

```
>>> bobs_secret_key = SecretKey.random()
>>> bobs_public_key = bobs_secret_key.public_key()
```

### Alice grants access to Bob by generating kfrags¶

When Alice wants to grant Bob access to view her encrypted data,
she creates *re-encryption key fragments*, or *“kfrags”*,
which are next sent to N proxies or *Ursulas*.

Alice must specify `shares`

(the total number of kfrags),
and a `threshold`

(the minimum number of kfrags needed to activate a capsule).
In the following example, Alice creates 20 kfrags,
but Bob needs to get only 10 re-encryptions to activate the capsule.

```
>>> from umbral import generate_kfrags
>>> kfrags = generate_kfrags(delegating_sk=alices_secret_key,
... receiving_pk=bobs_public_key,
... signer=alices_signer,
... threshold=10,
... shares=20)
```

### Bob receives a capsule¶

Next, let’s generate a key pair for Bob, and pretend to send him the capsule through a side channel like S3, IPFS, Google Cloud, Sneakernet, etc.

```
# Bob receives the capsule through a side-channel: IPFS, Sneakernet, etc.
capsule = <fetch the capsule through a side-channel>
```

### Bob fails to open the capsule¶

If Bob attempts to open a capsule that was not encrypted for his public key, or re-encrypted for him by Ursula, he will not be able to open it.

```
>>> fail = decrypt_original(delegating_sk=bobs_secret_key,
... capsule=capsule,
... ciphertext=ciphertext)
Traceback (most recent call last):
...
ValueError
```

### Ursulas perform re-encryption¶

Bob asks several Ursulas to re-encrypt the capsule so he can open it.
Each Ursula performs re-encryption on the capsule using the `kfrag`

provided by Alice, obtaining this way a “capsule fragment”, or `cfrag`

.
Let’s mock a network or transport layer by sampling `threshold`

random kfrags,
one for each required Ursula.

Bob collects the resulting cfrags from several Ursulas.
Bob must gather at least `threshold`

cfrags in order to open the capsule.

```
>>> import random
>>> kfrags = random.sample(kfrags, # All kfrags from above
... 10) # M - Threshold
>>> from umbral import reencrypt
>>> cfrags = list() # Bob's cfrag collection
>>> for kfrag in kfrags:
... cfrag = reencrypt(capsule=capsule, kfrag=kfrag)
... cfrags.append(cfrag) # Bob collects a cfrag
```

## Decryption¶

### Bob checks the capsule fragments¶

If Bob received the capsule fragments in serialized form, he can verify that they are valid and really originate from Alice, using Alice’s public keys.

```
>>> from umbral import CapsuleFrag
>>> suspicious_cfrags = [CapsuleFrag.from_bytes(bytes(cfrag)) for cfrag in cfrags]
>>> cfrags = [cfrag.verify(capsule,
... verifying_pk=alices_verifying_key,
... delegating_pk=alices_public_key,
... receiving_pk=bobs_public_key,
... )
... for cfrag in suspicious_cfrags]
```

### Bob opens the capsule¶

Finally, Bob decrypts the re-encrypted ciphertext using his key.

```
>>> from umbral import decrypt_reencrypted
>>> cleartext = decrypt_reencrypted(receiving_sk=bobs_secret_key,
... delegating_pk=alices_public_key,
... capsule=capsule,
... verified_cfrags=cfrags,
... ciphertext=ciphertext)
```