Skip to content

Commit

Permalink
Add rust example for simple roundtrip and Python examples. (#14)
Browse files Browse the repository at this point in the history
  • Loading branch information
coltfred authored Nov 21, 2023
1 parent 01467a6 commit 69e0518
Show file tree
Hide file tree
Showing 19 changed files with 2,962 additions and 13 deletions.
48 changes: 35 additions & 13 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

69 changes: 69 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# IronCore Labs Alloy SDK Examples

This directory contains some examples of using the IronCore Labs Alloy SDK to protect sensitive data.

This SDK can be used in two different modes: Standalone and SaaS Shield. In Standalone mode, you will need to provide
your own secrets. In SaaS Shield mode, the TSP will derive secrets for you.

# Standalone client

Standalone mode is designed to support fully offline programs and therefore does not require any additional setup.
In a production environment, you would have to manage secrets for your tenants, but this example provides dummy
secrets for convenience.

# SaaS Shield client

In order to use SaaS Shield mode, you need to run one or more _Tenant Security Proxies_ (TSPs) in your environment.
This service is provided as a Docker container, so it is easy to run the proxy on any computer that has Docker
installed. IronCore Labs hosts the Docker container on a publicly accessible container registry, so you can pull
the image from there and run it locally.

The TSP has a companion Docker container, the _Tenant Security Logdriver_ (LD) that runs alongside it in your environment.
It is also hosted on the same publicly accessible container registry.

In addition to the Docker containers, you need a configuration file that specifies how the TSP and LD should communicate
with the IronCore Labs Configuration Broker and Data Control Platform, which work together to enable the end-to-end
encryption that keeps all of the tenant KMS configuration information secure. To simplify the process of running
these examples, we have created a demo vendor and tenants that you can use for the examples; all the necessary
configuration information is included in the `demo-tsp.conf` file in this directory.
**NOTE:** Normally, the file containing the configuration would be generated by the vendor and loaded into a
Kubernetes secret or similar mechanism for securely loading the configuration into the docker container. We
have included this configuration in the repository as a convenience. Also note that these accounts are all
created in IronCore's staging infrastructure.

The following command will get a TSP and LD running together on your computer with the provided configuration.
The `docker-compose` command will pull both container images, then start them up together on a subnetwork, so they can
communicate with each other.

```bash
docker-compose -f docker-compose.yml up
```

The TSP will be listening locally on port 32804.

Once the TSP and LD are running, you can experiment with the example programs.

Each of the examples executes as an individual tenant of our demo SaaS vendor. There are six tenants defined;
their IDs are the following:

- tenant-gcp
- tenant-aws
- tenant-azure
- tenant-gcp-l
- tenant-aws-l
- tenant-azure-l

The last three are similar to the first three, but they have [key leasing](https://ironcorelabs.com/docs/saas-shield/what-is-key-leasing/) enabled.

By default, an example will use the `tenant-gcp` tenant. If you would like to experiment with a different tenant, just do:

```bash
export TENANT_ID=<select tenant ID>
```

before running the example.

# Additional Resources

If you would like some more in-depth information, our website features a section of technical
documentation about our different [products](https://ironcorelabs.com/docs/).
8 changes: 8 additions & 0 deletions examples/demo-tsp.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
SERVICE_ACCOUNT_ID=d413a647febbac39ae4760cb06046652eab75c215e587ff3
SERVICE_CONFIG_ID=777
SERVICE_ENCRYPTION_PRIVATE_KEY=YiqyMDbPmnD8hVGNd3Rq9X+jT3/gXln2IET9m5b97NA=
SERVICE_SIGNING_PRIVATE_KEY=9lAJ3a1Cb4xTL9jD/qdt53pBSG6m9ZNWWFs2WsMN2Mpp+huxRRRM3ry83nu6PfBg4WgBa9oB3V+XN+/BTlQLAg==
SERVICE_SEGMENT_ID=698
API_KEY=0WUaXesNgbTAuLwn
IRONCORE_ENV=stage
RUST_LOG=info
23 changes: 23 additions & 0 deletions examples/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
version: "3.3"
services:
tenant-security-proxy:
env_file:
- ./demo-tsp.conf
ports:
- 32804:7777
- 32805:9000
image: gcr.io/ironcore-images/tenant-security-proxy:4
links:
- tenant-security-logdriver
tenant-security-logdriver:
environment:
- LOGDRIVER_EVENT_PRODUCER_URL=tcp://tenant-security-proxy:5555
env_file:
- ./demo-tsp.conf
ports:
- 32806:9001
image: gcr.io/ironcore-images/tenant-security-logdriver:4
volumes:
- type: bind
source: /tmp
target: /logdriver
1 change: 1 addition & 0 deletions examples/python/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
ironcore_alloy_examples.egg-info/
48 changes: 48 additions & 0 deletions examples/python/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# Python Examples

## Setup

To run the examples, you must first activate the virtual environment; we use `hatch` to manage this.
If you don't have `hatch` installed, directions for installing it can be found
[here](https://hatch.pypa.io/latest/install/).

From the `examples/python` folder, run

```bash
hatch shell
```

Once the environment is activated, decide if you would like to run the Standalone or SaaS Shield examples and navigate
to the respective directory. If you choose SaaS Shield, be sure to startup the TSP as described [here](../README.md).

## Examples

### Vector roundtrip

This example shows creating a plaintext vector associated with a tenant, encrypting the vector, then decrypting it.
Because of floating point arithmetic, the decrypted vector may not perfectly match the plaintext.

```
python vector-roundtrip.py
```

Each time you run the example, the encrypted vector will change significantly, but nearest-neighbor searches
will still function.

### Vector search

This example shows creating a plaintext vector associated with a tenant, encrypting the vector, then generating
query vectors that can be used for nearest-neighbor searches on the original plaintext.

```
python vector-search.py
```

### Standard roundtrip

This example shows creating a plaintext document containing personal information associated with a tenant, encrypting
the document, then decrypting it.

```
python standard-roundtrip.py
```
8 changes: 8 additions & 0 deletions examples/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# hatch shell

[project]
name = "ironcore-alloy-examples"
authors = [{ name = "IronCore Labs", email = "[email protected]" }]
requires-python = ">=3.7"
dependencies = ["ironcore-alloy"]
dynamic = ["version"]
40 changes: 40 additions & 0 deletions examples/python/saas-shield/standard-roundtrip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import ironcore_alloy as alloy
import os
import asyncio
import json


async def main():
tenant_id = os.environ.get('TENANT_ID', 'tenant-gcp')
api_key = os.environ.get('API_KEY', '0WUaXesNgbTAuLwn')
tsp_uri = "http://localhost:32804"
approximation_factor = 7.2
config = alloy.SaasShieldConfiguration(tsp_uri, api_key, False, approximation_factor)
sdk = alloy.SaasShield(config)

jim_original = json.dumps({
"name": "Jim Bridger",
"address": "2825-519 Stone Creek Rd, Bozeman, MT 59715",
"ssn": "000-12-2345"
})
metadata = alloy.AlloyMetadata.new_simple(tenant_id)
# Encrypt Jim's personal information
encrypted = await sdk.standard().encrypt({"jim": bytes(jim_original, "utf-8")}, metadata)
# Store off the `document` and `edek`

# -----------------------------

# Later, retrieve the `document` and `edek`
encrypted_recreated = alloy.EncryptedDocument(encrypted.edek, encrypted.document)
# Decrypt Jim's personal information
decrypted = await sdk.standard().decrypt(encrypted_recreated, metadata)
jim_decrypted = json.loads(decrypted["jim"])

print("Decrypted SSN: ", jim_decrypted["ssn"])
print("Decrypted address: ", jim_decrypted["address"])
print("Decrypted name: ", jim_decrypted["name"])


if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
29 changes: 29 additions & 0 deletions examples/python/saas-shield/vector-roundtrip.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import ironcore_alloy as alloy
import os
import asyncio


async def main():
tenant_id = os.environ.get('TENANT_ID', 'tenant-gcp')
api_key = os.environ.get('API_KEY', '0WUaXesNgbTAuLwn')
tsp_uri = "http://localhost:32804"
approximation_factor = 7.2
config = alloy.SaasShieldConfiguration(tsp_uri, api_key, False, approximation_factor)
sdk = alloy.SaasShield(config)

data = [1.2, -1.23, 3.24, 2.37]
print("Plaintext vector: ", data)
plaintext = alloy.PlaintextVector(data, "contacts", "conversation-sentiment")
metadata = alloy.AlloyMetadata.new_simple(tenant_id)
encrypted = await sdk.vector().encrypt(plaintext, metadata)
print("Encrypted vector: ", encrypted.encrypted_vector)
# Store off encrypted_vector and paired_icl_info

decrypted = await sdk.vector().decrypt(encrypted, metadata)
print("Decrypted vector: ", decrypted.plaintext_vector)
print("Note that the encryption/decryption is lossy due to floating point math.")


if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
34 changes: 34 additions & 0 deletions examples/python/saas-shield/vector-search.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import ironcore_alloy as alloy
import os
import asyncio


async def main():
tenant_id = os.environ.get('TENANT_ID', 'tenant-gcp')
api_key = os.environ.get('API_KEY', '0WUaXesNgbTAuLwn')
tsp_uri = "http://localhost:32804"
approximation_factor = 7.2
config = alloy.SaasShieldConfiguration(tsp_uri, api_key, False, approximation_factor)
sdk = alloy.SaasShield(config)

data = [1.2, -1.23, 3.24, 2.37]
print("Plaintext vector: ", data)
plaintext = alloy.PlaintextVector(data, "contacts", "conversation-sentiment")
metadata = alloy.AlloyMetadata.new_simple(tenant_id)
encrypted = await sdk.vector().encrypt(plaintext, metadata)
print("Encrypted vector: ", encrypted.encrypted_vector)
# Store off `encrypted_vector` and `paired_icl_data`.
# Note that `paired_icl_data` is required for decryption. If you only need to support querying,
# then storing `paired_icl_data` is not required.

search_vector = alloy.PlaintextVector([1.4, -1.32, 4.32, 2.37], "contacts", "conversation-sentiment")
query_vectors = (await sdk.vector().generate_query_vectors({"vec_1": search_vector}, metadata))["vec_1"]
query_vectors_embeddings = map(lambda vector: vector.encrypted_vector, query_vectors)
print("Query vectors: ", list(query_vectors_embeddings))
print("Note that the query vectors are a nested list. If this tenant had an in-rotation key, two vectors")
print("would be in the result. In that case, both vectors must be used in conjunction when querying.")


if __name__ == '__main__':
loop = asyncio.get_event_loop()
loop.run_until_complete(main())
Loading

0 comments on commit 69e0518

Please sign in to comment.