├── .gitignore ├── README.md ├── batch_training ├── README.md ├── model_from_scratch │ ├── README.md │ ├── app │ │ ├── app.py │ │ └── params.json │ ├── app_token.json │ ├── invoke_app.py │ └── prequirements.txt └── sklearn │ ├── README.md │ ├── app │ ├── app.py │ └── model.pickle │ ├── app_token.json │ ├── invoke_app_sklearn.py │ ├── prep_sklearn_model.py │ ├── requirements.txt │ └── test_app_sklearn.py ├── credit_card_fraud_detection ├── README.md ├── app │ ├── app.py │ └── model.joblib ├── fraudulent_transaction.csv ├── legitimate_transaction.csv ├── requirements.txt └── train_model.py ├── echo ├── README.md ├── echo_deploy │ └── app.py ├── input.echo.art └── input.echo.data ├── hide_and_seek ├── README.md ├── requirements.txt └── seek │ ├── app.py │ └── secret.json ├── image_recognition ├── README.md ├── cat.jpg ├── coffee.jpg ├── deploy │ ├── app.py │ ├── labels.txt │ └── model.tflite ├── lizzard.jpg └── requirements.txt ├── isprime ├── README.md ├── input.isprime.data ├── isprime_deploy │ └── app.py └── isprime_js_deploy │ └── app.js ├── leader-election ├── README.md ├── function │ ├── app.py │ └── requirements.txt ├── gen_rsa_kay_pair.py ├── gossip │ ├── __init__.py │ └── gossip_protocol.py ├── requirements.txt └── start_node.py ├── logistic_regression_sklearn ├── README.md ├── app │ └── app.py ├── breast_cancer_data.csv └── requirements.txt ├── mortgage ├── README.md ├── app │ └── app.py └── input.mortgage.json ├── np-stats ├── README.md ├── app.py ├── numpy_serde.py ├── numpy_token.json ├── requirements.txt └── run.py ├── ocr ├── README.md ├── encrypt_run_ocr.mjs ├── encrypt_run_ocr.py ├── run_ocr.mjs └── run_ocr.py ├── pendulum ├── README.md ├── app.py └── requirements.txt ├── reader ├── deploy │ └── app.py └── requirements.txt ├── secure_search ├── README.md ├── app.py ├── hybrid_pke-1.0.0.dist-info │ ├── INSTALLER │ ├── METADATA │ ├── RECORD │ ├── REQUESTED │ ├── WHEEL │ └── license_files │ │ ├── LICENSE │ │ └── NOTICE ├── hybrid_pke │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ └── __init__.cpython-39.pyc │ └── hybrid_pke.cpython-39-x86_64-linux-gnu.so ├── input.search.data ├── public_keyset.bin ├── requirements.txt └── test │ ├── generate_keys.py │ ├── private_keyset.bin │ ├── public_keyset.bin │ ├── test_cape.py │ └── test_keys_locally.py └── sentiment_analysis ├── README.md ├── deploy ├── app.py ├── labels.txt ├── model.tflite └── vocab.txt ├── input.neg.data ├── input.pos.data └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Distribution / packaging 3 | .Python 4 | build/ 5 | develop-eggs/ 6 | dist/ 7 | downloads/ 8 | eggs/ 9 | .eggs/ 10 | parts/ 11 | sdist/ 12 | var/ 13 | wheels/ 14 | pip-wheel-metadata/ 15 | share/python-wheels/ 16 | *.egg-info/ 17 | .installed.cfg 18 | *.egg 19 | MANIFEST 20 | 21 | # PyInstaller 22 | # Usually these files are written by a python script from a template 23 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 24 | *.manifest 25 | *.spec 26 | 27 | # Installer logs 28 | pip-log.txt 29 | pip-delete-this-directory.txt 30 | 31 | # Unit test / coverage reports 32 | htmlcov/ 33 | .tox/ 34 | .nox/ 35 | .coverage 36 | .coverage.* 37 | .cache 38 | nosetests.xml 39 | coverage.xml 40 | *.cover 41 | *.py,cover 42 | .hypothesis/ 43 | .pytest_cache/ 44 | 45 | # Translations 46 | *.mo 47 | *.pot 48 | 49 | # Django stuff: 50 | *.log 51 | local_settings.py 52 | db.sqlite3 53 | db.sqlite3-journal 54 | 55 | # Flask stuff: 56 | instance/ 57 | .webassets-cache 58 | 59 | # Scrapy stuff: 60 | .scrapy 61 | 62 | # Sphinx documentation 63 | docs/_build/ 64 | 65 | # PyBuilder 66 | target/ 67 | 68 | # Jupyter Notebook 69 | .ipynb_checkpoints 70 | 71 | # IPython 72 | profile_default/ 73 | ipython_config.py 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # pipenv 79 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 80 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 81 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 82 | # install all needed dependencies. 83 | #Pipfile.lock 84 | 85 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 86 | __pypackages__/ 87 | 88 | # Celery stuff 89 | celerybeat-schedule 90 | celerybeat.pid 91 | 92 | # SageMath parsed files 93 | *.sage.py 94 | 95 | # Environments 96 | .env 97 | .venv 98 | env/ 99 | venv/ 100 | ENV/ 101 | env.bak/ 102 | venv.bak/ 103 | 104 | # Spyder project settings 105 | .spyderproject 106 | .spyproject 107 | 108 | # Rope project settings 109 | .ropeproject 110 | 111 | # mkdocs documentation 112 | /site 113 | 114 | # mypy 115 | .mypy_cache/ 116 | .dmypy.json 117 | dmypy.json 118 | 119 | # Pyre type checker 120 | .pyre/ 121 | 122 | .DS_store 123 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cape Functions 2 | 3 | ## Getting Started 4 | 5 | To run these functions with Cape, you need to first install the [Cape CLI](https://docs.capeprivacy.com/getting-started#install-the-cape-cli). Then you will have to sign up from [Cape's website](https://capeprivacy.com/) or using the CLI: 6 | ``` 7 | cape signup 8 | ``` 9 | 10 | These examples can be also executed from Cape's SDKs: [cape-js](https://docs.capeprivacy.com/sdks/javascript-sdk) and [pycape](https://pydocs.capeprivacy.com/). If you execute these functions from one of the SDK, you will have to generate a [personal access token](https://docs.capeprivacy.com/reference/user-tokens). This can be done from [Cape's website](https://capeprivacy.com/) or from the CLI: 11 | ``` 12 | cape token create --name my-token --description 'for use in the javascript sdk' 13 | ``` 14 | 15 | ## Examples 16 | 17 | ### Echo 18 | 19 | A simple function that returns whatever you send it. Check out the folder [echo](./echo/) to learn how to deploy this function. To invoke this function, you can run the following: 20 | 21 | ``` 22 | cape run capedocs/echo -f echo/input.echo.data 23 | ``` 24 | 25 | ### Isprime 26 | 27 | A simple function checking if your number is prime or not. Check out the folder [isprime](./isprime/) to learn how to deploy this function. To invoke this function, you can run the following: 28 | 29 | ``` 30 | cape run capedocs/isprime -f isprime/input.isprime.data 31 | ``` 32 | 33 | ### Pendulum 34 | In this example, we show how to run a function requiring a dependency. The function simply returns the current time based on a specific timezone using the [Pendulum](https://pendulum.eustace.io/) library. To learn how to deploy this function, check out the folder [pendulum](./pendulum/). To invoke the function, invoke: 35 | 36 | ``` 37 | cape run capedocs/pendulum Europe/Paris 38 | ``` 39 | 40 | ### Numpy Stats 41 | A simple example with numpy dependencies and using [pycape](https://github.com/capeprivacy/pycape) and [serdio.lift_io](https://pydocs.capeprivacy.com/serdio.io_lifter.html#serdio.io_lifter.lift_io) to handle automatic serialization/deserialization of Cape function input/outputs. You can learn how to deploy this function by checking the folder [np-stats](./np-stats/). You can run the function as follow after install installing in a virtual environment in `np-stats/requirements.txt`: 42 | 43 | ``` 44 | export TOKEN= 45 | export FUNCTION_ID=capedocs/np-stats 46 | python np-stats/run.py 47 | ``` 48 | 49 | ### Secure Search 50 | Simulates a cybersecurity search function, where the IP addresses you are interested in need to remain private but the data/logs you are searching might be public. You can learn more about this function by checking the folder [secure-search](./secure_search/). 51 | 52 | ``` 53 | cape run capedocs/secure-search -f secure_search/input.search.data 54 | ``` 55 | 56 | ### Leader Election 57 | Demos how a secure trusted execution environment like Cape can be leveraged in consensus in order to guarantee fairness. Details on how to run this example can be found 58 | [here](./leader-election). 59 | 60 | 61 | ### Hide-and-seek 62 | To learn more about the confidential hide-and-seek example, you can check the folder [hide_and_seek](./hide_and_seek). 63 | 64 | 65 | ### Mortgage 66 | This application is a mortgage calculator that computes if an applicant is eligible for a mortgage. To learn how to deploy this application, you can check out the folder [mortgage](./mortgage). To call this function, you can run: 67 | ``` 68 | cape run capedocs/mortgage -f mortgage/input.mortgage.json 69 | ``` 70 | 71 | ## Machine Learning Examples 72 | 73 | ### Image Classification Inference with ONNX 74 | 75 | To learn how you can deploy and invoke an image classification model using the [onnxruntime](https://onnxruntime.ai/), you can check the [capeprivacy/image-classification-onnx](image-classification-onnxhttps://github.com/capeprivacy/image-classification-onnx) repository. 76 | 77 | 78 | ### Image Classification Inference with tflite 79 | This example demonstrates how to deploy and invoke an image classification model with [tflite](https://www.tensorflow.org/lite). To learn how to deploy this application, you can check out the folder [image-classification](./image_recognition). To invoke the model, run: 80 | 81 | ``` 82 | cape run capedocs/image-recognition -f image_recognition/coffee.jpg 83 | 84 | ('Image Label is :', 'espresso', ', with Accuracy :', 84.38, '%.') 85 | ``` 86 | 87 | ### Sentiment Analysis Inference with tflite 88 | This example demonstrates how you can deploy and invoke a sentiment analysis model with [tflite](https://www.tensorflow.org/lite). To learn how to deploy this application, you can check out the folder [sentiment_analysis](./sentiment_analysis). To invoke the model, simply run: 89 | 90 | ``` 91 | cape run capedocs/sentiment-analysis -f sentiment_analysis/input.pos.data 92 | 93 | ('The sentiment is: ', 'positive', ' with a probability of ', 78.08290123939514, '%.') 94 | ``` 95 | 96 | ### Training and Inference with Scikit-Learn. 97 | You can check the following examples to learn about how to deploying and invoke machine learning models with Scikit-Learn: 98 | - [credit_card_fraud_detection](./credit_card_fraud_detection/): performs secure inference to classify credit card transactions as fraudulent or legitimate. 99 | - [logistic_regression_sklearn](./logistic_regression_sklearn/): securely train an Sklearn logistic regression model on the breast cancer dataset. 100 | - [batch_training](./batch_training/): shows how to perform batch training with Sklearn model. 101 | 102 | 103 | # Invoke Cape's Confidential Services 104 | ### Invoke Cape's Confidential OCR Service from SDKs 105 | 106 | This example shows you how you can run the [Cape's confidential optical character recognition service](https://docs.capeprivacy.com/cape-hosted/ocr) from the SDKs: [cape-js](https://docs.capeprivacy.com/sdks/javascript-sdk) and [pycape](https://pydocs.capeprivacy.com/). 107 | 108 | For this example, we will run the OCR on the PDF `./ocr/claude_shannon.pdf`. 109 | 110 | **From cape-js:** 111 | 112 | Before invoking the OCR, set the environment variable `CAPE_AUTH_TOKEN` to your [personal access token](https://docs.capeprivacy.com/reference/user-tokens). 113 | ``` 114 | export CAPE_AUTH_TOKEN="your cape auth token" 115 | ``` 116 | 117 | To run the OCR from cape-js on the PDF, from the [ocr folder](./ocr), run: 118 | ``` 119 | node run_ocr.mjs 120 | ``` 121 | 122 | To encrypt the PDF with [cape.encrypt](https://docs.capeprivacy.com/tutorials/encrypting#cape-encrypt), then invoke the OCR on the encrypted PDF, run: 123 | ``` 124 | node encrypt_run_ocr.mjs 125 | ``` 126 | 127 | **From pycape:** 128 | 129 | Before invoking the OCR, set the environment variable `CAPE_AUTH_TOKEN` to your [personal access token](https://docs.capeprivacy.com/reference/user-tokens). 130 | 131 | ``` 132 | export CAPE_AUTH_TOKEN="your cape auth token" 133 | ``` 134 | 135 | To run the OCR from pycape on the PDF, from the [ocr folder](./ocr), run: 136 | ``` 137 | python run_ocr.py 138 | ``` 139 | 140 | To encrypt the PDF with [cape.encrypt](https://docs.capeprivacy.com/tutorials/encrypting#cape-encrypt), then invoke the OCR on the encrypted PDF, run: 141 | ``` 142 | python encrypt_run_ocr.py 143 | ``` 144 | 145 | 146 | -------------------------------------------------------------------------------- /batch_training/README.md: -------------------------------------------------------------------------------- 1 | # Batch training 2 | 3 | This readme describes how to perform batch training that allows for training a model within an enclave on virtually infinite data sizes. -------------------------------------------------------------------------------- /batch_training/model_from_scratch/README.md: -------------------------------------------------------------------------------- 1 | # Batch training 2 | 3 | This readme describes how to perform batch training on a logistic regression model created from scratch 4 | 5 | ## How to perform batch training with Cape 6 | 7 | ### Prepare deployment folder with dependencies 8 | Create a depoyment folder called `app`: 9 | ``` 10 | mkdir app 11 | ``` 12 | Save app.py into the folder: 13 | ``` 14 | cp app.py ./app 15 | ``` 16 | Save untrained model into the folder: 17 | ``` 18 | cp params.json ./app 19 | ``` 20 | Install necessary libraries listed in `requirements.txt` 21 | ``` 22 | sudo docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target ./app/ 23 | ``` 24 | 25 | ### Login and deploy with Cape 26 | ``` 27 | cape login 28 | 29 | Your CLI confirmation code is: GQDK-SNHL 30 | Visit this URL to complete the login process: https://login.capeprivacy.com/activate?user_code=GQDK-SNHL 31 | Congratulations, you're all set! 32 | ``` 33 | 34 | ``` 35 | cape deploy ./app 36 | 37 | Deploying function to Cape ... 38 | Success! Deployed function to Cape. 39 | Function ID ➜ Laj8YqrvCaK2izYmnbsbdX 40 | Function Checksum ➜ 798bc8e03b3dac51967607877fb3ac4be8acf5e02aae9699ec8fccced2f7b0ac 41 | ``` 42 | 43 | ### Generate token 44 | 45 | ``` 46 | cape token --function-checksum -o json > app_token.json 47 | ``` 48 | 49 | The file `app_token.json` will be used to create a function reference `pycape.FunctionRef` for estabilishing a connection with the enclave. 50 | 51 | ### Invoke the deployed Cape function from PyCape 52 | ``` 53 | python3 invoke_app.py 54 | ``` 55 | This script will invoke the Cape function repeatedly to train the model batch by batch. After training is completed, the model is printed out as a json file. -------------------------------------------------------------------------------- /batch_training/model_from_scratch/app/app.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import copy 4 | import json 5 | 6 | class LogisticRegression(): 7 | def __init__(self): 8 | self.losses = [] 9 | self.train_accuracies = [] 10 | self.weights = [] 11 | self.bias = 0 12 | 13 | def accuracy_score(self, y_true, y_pred): 14 | correct = np.sum(y_true == y_pred) 15 | accuracy = correct/y_true.shape[0] 16 | return accuracy 17 | 18 | def fit(self, x, y, epochs): 19 | x = self._transform_x(x) 20 | y = self._transform_y(y) 21 | 22 | self.weights = np.zeros(x.shape[1]) 23 | self.bias = 0 24 | 25 | for i in range(epochs): 26 | x_dot_weights = np.matmul(self.weights, x.transpose()) + self.bias 27 | pred = self._sigmoid(x_dot_weights) 28 | loss = self.compute_loss(y, pred) 29 | error_w, error_b = self.compute_gradients(x, y, pred) 30 | self.update_model_parameters(error_w, error_b) 31 | 32 | pred_to_class = [1 if p > 0.5 else 0 for p in pred] 33 | self.train_accuracies.append(self.accuracy_score(y, pred_to_class)) 34 | self.losses.append(loss) 35 | 36 | def compute_loss(self, y_true, y_pred): 37 | # binary cross entropy 38 | y_zero_loss = y_true * np.log(y_pred + 1e-9) 39 | y_one_loss = (1-y_true) * np.log(1 - y_pred + 1e-9) 40 | return -np.mean(y_zero_loss + y_one_loss) 41 | 42 | def compute_gradients(self, x, y_true, y_pred): 43 | # derivative of binary cross entropy 44 | difference = y_pred - y_true 45 | gradient_b = np.mean(difference) 46 | gradients_w = np.matmul(x.transpose(), difference) 47 | gradients_w = np.array([np.mean(grad) for grad in gradients_w]) 48 | 49 | return gradients_w, gradient_b 50 | 51 | def update_model_parameters(self, error_w, error_b): 52 | self.weights = self.weights - 0.1 * error_w 53 | self.bias = self.bias - 0.1 * error_b 54 | 55 | def predict(self, x): 56 | x_dot_weights = np.matmul(x, self.weights.transpose()) + self.bias 57 | probabilities = self._sigmoid(x_dot_weights) 58 | return [1 if p > 0.5 else 0 for p in probabilities] 59 | 60 | def _sigmoid(self, x): 61 | return np.array([self._sigmoid_function(value) for value in x]) 62 | 63 | def _sigmoid_function(self, x): 64 | if x >= 0: 65 | z = np.exp(-x) 66 | return 1 / (1 + z) 67 | else: 68 | z = np.exp(x) 69 | return z / (1 + z) 70 | 71 | def _transform_x(self, x): 72 | x = copy.deepcopy(x) 73 | return x.values 74 | 75 | def _transform_y(self, y): 76 | y = copy.deepcopy(y) 77 | return y.values.reshape(y.shape[0], 1) 78 | 79 | 80 | def cape_handler(batch): 81 | batch = batch.decode() 82 | data = pd.read_json(batch) 83 | # data = pd.read_csv("../batch_small.csv") 84 | 85 | data_size = data.shape[0] 86 | test_split = 0.33 87 | test_size = int(data_size * test_split) 88 | 89 | choices = np.arange(0, data_size) 90 | test = np.random.choice(choices, test_size, replace=False) 91 | train = np.delete(choices, test) 92 | 93 | test_set = data.iloc[test] 94 | train_set = data.iloc[train] 95 | 96 | column_names = list(data.columns.values) 97 | features = column_names[1:len(column_names)-1] 98 | 99 | y_train = train_set["Class"] 100 | y_test = test_set["Class"] 101 | X_train = train_set[features] 102 | X_test = test_set[features] 103 | 104 | lr = LogisticRegression() 105 | # Load model parameters 106 | with open("params.json") as f: 107 | params = f.read() 108 | params = json.loads(params) 109 | lr.weights = params["weights"] 110 | lr.bias = params["bias"] 111 | 112 | # Fit model to current batch 113 | lr.fit(X_train, y_train, epochs=150) 114 | 115 | pred = lr.predict(X_test) 116 | accuracy = lr.accuracy_score(y_test, pred) 117 | 118 | # Save model parameters 119 | params["weights"] = lr.weights.tolist() 120 | params["bias"] = lr.bias.tolist() 121 | with open("params.json", "w") as outfile: 122 | json.dump(params, outfile) 123 | 124 | # Trained model 125 | model = {"accuracy": accuracy, "weights": lr.weights.tolist(), "bias": lr.bias.tolist()} 126 | return model 127 | 128 | # print(cape_handler("batch")) -------------------------------------------------------------------------------- /batch_training/model_from_scratch/app/params.json: -------------------------------------------------------------------------------- 1 | {"weights": [-433.65000000000003, 1.0475666367048733, -1.2261240897365155, -5.489119193218135, -2.313557604198899, 0.6059759030536994, -1.0947533117110442, -1.4714798902931425, 0.12858237803079262, 0.05980388753133963, -0.12872998634892854, -0.9624988866079761, -2.172438937306101, 0.5445200927925031, 0.709838650066208, -2.1221767996369434, 1.8859827468807682, -0.060405585937994034, 2.0337007472991817, 0.5393413947689336, -0.154473311534277, -0.23764723214731226, 0.20432149628265428, 0.2939155770044103, -0.5543142334620477, -1.0135671169045322, 0.1293676372776513, 0.020672029955738004, -0.020787359495535142, -574.9484999999999], "bias": -0.05} -------------------------------------------------------------------------------- /batch_training/model_from_scratch/app_token.json: -------------------------------------------------------------------------------- 1 | {"function_id":"Laj8YqrvCaK2izYmnbsbdX","function_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE4NzAxMTc1OTksImlhdCI6MTY2OTc0MTE3OSwiaXNzIjoiZ2l0aHVifDI2MzY4MjQ5Iiwic2NvcGUiOiJmdW5jdGlvbjppbnZva2UiLCJzdWIiOiJMYWo4WXFydkNhSzJpelltbmJzYmRYIn0.LMe_2UX8nuYsmRkQbVOZyLZsIq2iyviq5QEloU2I8XToidb0504yiPAfVWVHgHZ3-O181NwPX574Yjay8GO0025K9uWPwVCFLRoRqa9LKpt5_oCyK2IFSbzQlt1bVM_2gDhi8VA1DbUkcnLCunxd89hVwKBfLaJtx3P6V6cXsJ3f1vn_0pXfS6U_xqIAiuqEm8lJbzA1By434O0KNibktaO4YpWxvs7TW1Uv4ivFTkWQAV-TJ-vr4cKvMOJu6FQcI-D9JhlwHmcmpvFXTtWRGBm_AN4RvuiWhLVGyVCUvkziBQeoCDrC1_2AaYdaa-lDiaCnPmuO7uCc_4wE4wVmKA","function_checksum":"798bc8e03b3dac51967607877fb3ac4be8acf5e02aae9699ec8fccced2f7b0ac"} 2 | -------------------------------------------------------------------------------- /batch_training/model_from_scratch/invoke_app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | from tqdm.notebook import tqdm 4 | import pandas as pd 5 | 6 | from pycape import Cape 7 | from pycape import FunctionRef 8 | 9 | if __name__ == "__main__": 10 | url = os.environ.get("CAPE_HOST", "https://app.capeprivacy.com") 11 | function_json = os.environ.get("FUNCTION_JSON", "app_token.json") 12 | function_json = pathlib.Path(__file__).parent.absolute() / function_json 13 | function_ref = FunctionRef.from_json(function_json) 14 | cape = Cape(url=url) 15 | cape.connect(function_ref) 16 | 17 | batch_size = 500 18 | for batch_df in tqdm(pd.read_csv("train_data.csv", chunksize=batch_size, iterator=True)): 19 | df_byte = batch_df.to_json().encode() 20 | clf = cape.invoke(df_byte) 21 | 22 | # trained model 23 | print(clf.decode()) 24 | cape.close() -------------------------------------------------------------------------------- /batch_training/model_from_scratch/prequirements.txt: -------------------------------------------------------------------------------- 1 | pandas 2 | numpy -------------------------------------------------------------------------------- /batch_training/sklearn/README.md: -------------------------------------------------------------------------------- 1 | # Batch training 2 | 3 | This readme describes how to perform batch training on an Sklearn model with pycape 4 | 5 | ## How to perform batch training with Cape on sklearn models 6 | 7 | ### Prepare sklearn model 8 | Run: 9 | ``` 10 | python3 prep_sklearn_model.py 11 | ``` 12 | In this script you specify the sklearn model type and save it as a pickle. This untrained pickled model will be deployed into the enclave, where it will be trained. 13 | 14 | E.g.: 15 | ``` 16 | # initialize model 17 | clf = SGDClassifier(alpha=.0001, loss='log', penalty='l2', n_jobs=-1, shuffle=True) 18 | 19 | # save model 20 | with open("model.pickle", "wb") as f: 21 | pickle.dump(clf, f) 22 | ``` 23 | ### Prepare app.py 24 | The `cape_hander()` function in `app.py` will receive a batch of training data, load the pickled model, train it on that batch, save the model as a pickle, and return it. 25 | E.g.: 26 | ``` 27 | def cape_handler(batch): 28 | # accept batch input training data 29 | batch = batch.decode() 30 | data = pd.read_json(batch) 31 | 32 | # load model 33 | with open("model.pickle", "rb") as f: 34 | clf = pickle.load(f) 35 | 36 | # prep batch data 37 | X = data.drop(['Class'], axis=1) 38 | Y = data["Class"] 39 | X_data = X.values 40 | Y_data = Y.values 41 | 42 | # train 43 | clf.partial_fit(X, Y, classes=[0,1]) 44 | 45 | # save model 46 | with open("model", "wb") as f: 47 | pickle.dump(clf, f) 48 | 49 | # return model 50 | s = pickle.dumps(clf) 51 | return s 52 | ``` 53 | 54 | ### Prepare deployment folder with dependencies 55 | Create a depoyment folder called `app`: 56 | ``` 57 | mkdir app 58 | ``` 59 | Save app.py into the folder: 60 | ``` 61 | cp app.py ./app 62 | ``` 63 | Save untrained model into the folder: 64 | ``` 65 | cp model.pickle ./app 66 | ``` 67 | Install necessary libraries listed in `requirements.txt` 68 | ``` 69 | sudo docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target ./app/ 70 | ``` 71 | 72 | ### Login and deploy with Cape 73 | ``` 74 | cape login 75 | 76 | Your CLI confirmation code is: GQDK-SNHL 77 | Visit this URL to complete the login process: https://login.capeprivacy.com/activate?user_code=GQDK-SNHL 78 | Congratulations, you're all set! 79 | ``` 80 | 81 | ``` 82 | cape deploy ./app 83 | 84 | Deploying function to Cape ... 85 | Success! Deployed function to Cape. 86 | Function ID ➜ NxnusuJ8caocbx9ckEg5tb 87 | Function Checksum ➜ bf750d97b35fe9973e580949e4143ea5350df2d3ca607f85aecebbe016ac1eb1 88 | ``` 89 | 90 | ### Generate token 91 | 92 | ``` 93 | cape token --function-checksum -o json > app_token.json 94 | ``` 95 | 96 | The file `app_token.json` will be used to create a function reference `pycape.FunctionRef` for estabilishing a connection with the enclave. 97 | 98 | ### Invoke the deployed Cape function from PyCape 99 | ``` 100 | CAPE_DEV_DISABLE_SSL=True python3 invoke_app_sklearn.py 101 | ``` 102 | This script will invoke the Cape function repeatedly to train the model batch by batch. After training is completed, the model is saved as a pickle file. 103 | 104 | E.g.: 105 | ``` 106 | import os 107 | import pathlib 108 | from tqdm.notebook import tqdm 109 | import pandas as pd 110 | import pickle 111 | 112 | from pycape import Cape 113 | from pycape import FunctionRef 114 | 115 | if __name__ == "__main__": 116 | url = os.environ.get("CAPE_HOST", "https://k8s-cape-enclaver-750003af11-e3080498c852b366.elb.us-east-1.amazonaws.com") 117 | function_json = os.environ.get("FUNCTION_JSON", "sklearn_token.json") 118 | function_json = pathlib.Path(__file__).parent.absolute() / function_json 119 | function_ref = FunctionRef.from_json(function_json) 120 | cape = Cape(url=url) 121 | cape.connect(function_ref) 122 | 123 | # set batch size 124 | batch_size = 500 125 | 126 | # train model in batches 127 | for batch_df in tqdm(pd.read_csv("training_data.csv", chunksize=batch_size, iterator=True)): 128 | df_byte = batch_df.to_json().encode() 129 | clf = cape.invoke(df_byte) 130 | clf = pickle.loads(clf) 131 | 132 | # save model 133 | with open("trained_sklearn_model.pickle", "wb") as f: 134 | pickle.dump(clf, f) 135 | ``` 136 | 137 | ### Test the trained model 138 | ``` 139 | python3 test_app_sklearn.py 140 | ``` 141 | 142 | E.g.: 143 | ``` 144 | import pickle 145 | import sklearn 146 | import pandas as pd 147 | from sklearn.model_selection import train_test_split 148 | from sklearn.linear_model import SGDClassifier 149 | from sklearn.metrics import accuracy_score 150 | 151 | # load the model trained in enclave 152 | with open("trained_sklearn_model.pickle", "rb") as f: 153 | model = pickle.load(f) 154 | 155 | # prepare testing data 156 | data = pd.read_csv("testing_data.csv") 157 | X = data.drop(['Class'], axis=1) 158 | Y = data["Class"] 159 | X_data = X.values 160 | Y_data = Y.values 161 | 162 | # run prediction and compute accuracy 163 | y_pred = model.predict(X_data) 164 | print(accuracy_score(Y_data, y_pred)) 165 | ``` -------------------------------------------------------------------------------- /batch_training/sklearn/app/app.py: -------------------------------------------------------------------------------- 1 | # import necessary packages 2 | import sklearn 3 | from sklearn.linear_model import SGDClassifier 4 | import pandas as pd 5 | import pickle 6 | 7 | def cape_handler(batch): 8 | # accept batch input training data 9 | batch = batch.decode() 10 | data = pd.read_json(batch) 11 | 12 | # load model 13 | with open("model.pickle", "rb") as f: 14 | clf = pickle.load(f) 15 | 16 | # prep batch data 17 | X = data.drop(['Class'], axis=1) 18 | Y = data["Class"] 19 | X_data = X.values 20 | Y_data = Y.values 21 | 22 | # train 23 | clf.partial_fit(X, Y, classes=[0,1]) 24 | 25 | # save model 26 | with open("model", "wb") as f: 27 | pickle.dump(clf, f) 28 | 29 | # return model 30 | s = pickle.dumps(clf) 31 | return s -------------------------------------------------------------------------------- /batch_training/sklearn/app/model.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/batch_training/sklearn/app/model.pickle -------------------------------------------------------------------------------- /batch_training/sklearn/app_token.json: -------------------------------------------------------------------------------- 1 | {"function_id":"NxnusuJ8caocbx9ckEg5tb","function_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE4NzAxMTcxNDgsImlhdCI6MTY2OTc0MDcyOSwiaXNzIjoiZ2l0aHVifDI2MzY4MjQ5Iiwic2NvcGUiOiJmdW5jdGlvbjppbnZva2UiLCJzdWIiOiJOeG51c3VKOGNhb2NieDlja0VnNXRiIn0.hCZCdd1sq41HKyuY771LtBl_cwj6QxvfL68hdErwm41eB5NQHj0MSzc3IrMc3d2DyouStWNYpGUeJ9c3Xl_cqEgrEQ5OWuYP4sPin2craOwdHksx6F90J94urilq5YJL8eN6LDlGHTup1WcE3kLwsYlUUtfxxBlOvSqeaUJmvAW0XYYUhFr5WLwuzsma0Gl3LR2hS_dNwLtpTSjVFZdxnpDgG2IpP8zeQZHBYbkXHbILEw19tZ_QUwdcOliB8yiXIa_ZSOO7_G5ny0TLMNlQLij5RDhTSVZssJMj7-K52T-_xyEoIAJkJLqKA6whL0n63qT__muGn49Ip6q8Hgx_0w","function_checksum":"bf750d97b35fe9973e580949e4143ea5350df2d3ca607f85aecebbe016ac1eb1"} 2 | -------------------------------------------------------------------------------- /batch_training/sklearn/invoke_app_sklearn.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | from tqdm.notebook import tqdm 4 | import pandas as pd 5 | import pickle 6 | 7 | from pycape import Cape 8 | from pycape import FunctionRef 9 | 10 | if __name__ == "__main__": 11 | url = os.environ.get("CAPE_HOST", "https://k8s-cape-enclaver-750003af11-e3080498c852b366.elb.us-east-1.amazonaws.com") 12 | function_json = os.environ.get("FUNCTION_JSON", "app_token.json") 13 | function_json = pathlib.Path(__file__).parent.absolute() / function_json 14 | function_ref = FunctionRef.from_json(function_json) 15 | cape = Cape(url=url) 16 | cape.connect(function_ref) 17 | 18 | # set batch size 19 | batch_size = 500 20 | 21 | # train model in batches 22 | for batch_df in tqdm(pd.read_csv("training_data.csv", chunksize=batch_size, iterator=True)): 23 | df_byte = batch_df.to_json().encode() 24 | clf = cape.invoke(df_byte) 25 | clf = pickle.loads(clf) 26 | 27 | # save model 28 | with open("trained_sklearn_model.pickle", "wb") as f: 29 | pickle.dump(clf, f) -------------------------------------------------------------------------------- /batch_training/sklearn/prep_sklearn_model.py: -------------------------------------------------------------------------------- 1 | """ 2 | Prepare an sklearn model: (1) choose a model type, (2) save it as a pickle that will be deployed to enclave 3 | """ 4 | 5 | import pickle 6 | from sklearn.linear_model import SGDClassifier 7 | 8 | # initialize model 9 | clf = SGDClassifier(alpha=.0001, loss='log', penalty='l2', n_jobs=-1, shuffle=True) 10 | # save model 11 | with open("model.pickle", "wb") as f: 12 | pickle.dump(clf, f) -------------------------------------------------------------------------------- /batch_training/sklearn/requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.23.5 2 | pandas==1.5.1 3 | python-dateutil==2.8.2 4 | pytz==2022.6 5 | scikit-learn==1.1.3 6 | scipy==1.9.3 7 | six==1.16.0 8 | sklearn==0.0.post1 9 | threadpoolctl==3.1.0 10 | -------------------------------------------------------------------------------- /batch_training/sklearn/test_app_sklearn.py: -------------------------------------------------------------------------------- 1 | import pickle 2 | import sklearn 3 | import pandas as pd 4 | from sklearn.model_selection import train_test_split 5 | from sklearn.linear_model import SGDClassifier 6 | from sklearn.metrics import accuracy_score 7 | 8 | # load the model trained in enclave 9 | with open("trained_sklearn_model.pickle", "rb") as f: 10 | model = pickle.load(f) 11 | 12 | # prepare testing data 13 | data = pd.read_csv("testing_data.csv") 14 | X = data.drop(['Class'], axis=1) 15 | Y = data["Class"] 16 | X_data = X.values 17 | Y_data = Y.values 18 | 19 | # run prediction and compute accuracy 20 | y_pred = model.predict(X_data) 21 | print(accuracy_score(Y_data, y_pred)) -------------------------------------------------------------------------------- /credit_card_fraud_detection/README.md: -------------------------------------------------------------------------------- 1 | # Credit Card Fraud Detection: Random Forest Clasifier Inference with Sklearn 2 | 3 | This application performs secure inference to classify credit card transactions as fraudulent or legitimate. 4 | The model is trained with `train_model.py`, which uses `Sklearn` random forest classifier. 5 | 6 | ## Usage 7 | 8 | ### Prepare deployment folder with dependencies 9 | ``` 10 | sudo docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target ./app/ 11 | ``` 12 | 13 | ### Deploy and run with Cape 14 | ``` 15 | cape login 16 | 17 | Your CLI confirmation code is: BZCS-NLTD 18 | Visit this URL to complete the login process: https://login.capeprivacy.com/activate?user_code=BZCS-NLTD 19 | Congratulations, you're all set! 20 | ``` 21 | 22 | ``` 23 | cape deploy ./app 24 | 25 | Deploying function to Cape ... 26 | Success! Deployed function to Cape. 27 | Function ID ➜ huM9vYySCV62Ln98wYiUfD 28 | Function Checksum ➜ 0d5c578b20f9fcfec77fcc2d950eacbf267a4401694b93a9af1c1ca7d347aa01 29 | ``` 30 | 31 | ``` 32 | cape run huM9vYySCV62Ln98wYiUfD -f fraudulent_transaction.csv --insecure -u https://k8s-cape-enclaver-750003af11-e3080498c852b366.elb.us-east-1.amazonaws.com 33 | 34 | This credit card transaction is fraudulent 35 | ``` 36 | 37 | ``` 38 | cape run huM9vYySCV62Ln98wYiUfD -f legitimate_transaction.csv --insecure -u https://k8s-cape-enclaver-750003af11-e3080498c852b366.elb.us-east-1.amazonaws.com 39 | 40 | This credit card transaction is legitimate 41 | ``` -------------------------------------------------------------------------------- /credit_card_fraud_detection/app/app.py: -------------------------------------------------------------------------------- 1 | from sklearn.metrics import accuracy_score 2 | from joblib import load 3 | import pandas as pd 4 | 5 | 6 | def cape_handler(input_data): 7 | csv = input_data.decode("utf-8") 8 | csv = csv.replace("\\t", ",").replace("\\n", "\n") 9 | f = open("data.csv", "w") 10 | f.write(csv) 11 | f.close() 12 | 13 | data = pd.read_csv("data.csv") 14 | clf = load('model.joblib') 15 | y_pred = clf.predict(data) 16 | if y_pred == 0: 17 | return "This credit card transaction is legitimate" 18 | else: 19 | return "This credit card transaction is fraudulent" -------------------------------------------------------------------------------- /credit_card_fraud_detection/app/model.joblib: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/credit_card_fraud_detection/app/model.joblib -------------------------------------------------------------------------------- /credit_card_fraud_detection/fraudulent_transaction.csv: -------------------------------------------------------------------------------- 1 | Time,V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16,V17,V18,V19,V20,V21,V22,V23,V24,V25,V26,V27,V28,Amount 2 | 406,-2.3122265423263,1.95199201064158,-1.60985073229769,3.9979055875468,-0.522187864667764,-1.42654531920595,-2.53738730624579,1.39165724829804,-2.77008927719433,-2.77227214465915,3.20203320709635,-2.89990738849473,-0.595221881324605,-4.28925378244217,0.389724120274487,-1.14074717980657,-2.83005567450437,-0.0168224681808257,0.416955705037907,0.126910559061474,0.517232370861764,-0.0350493686052974,-0.465211076182388,0.320198198514526,0.0445191674731724,0.177839798284401,0.261145002567677,-0.143275874698919,0 3 | -------------------------------------------------------------------------------- /credit_card_fraud_detection/legitimate_transaction.csv: -------------------------------------------------------------------------------- 1 | Time,V1,V2,V3,V4,V5,V6,V7,V8,V9,V10,V11,V12,V13,V14,V15,V16,V17,V18,V19,V20,V21,V22,V23,V24,V25,V26,V27,V28,Amount 2 | 0,-1.3598071336738,-0.0727811733098497,2.53634673796914,1.37815522427443,-0.338320769942518,0.462387777762292,0.239598554061257,0.0986979012610507,0.363786969611213,0.0907941719789316,-0.551599533260813,-0.617800855762348,-0.991389847235408,-0.311169353699879,1.46817697209427,-0.470400525259478,0.207971241929242,0.0257905801985591,0.403992960255733,0.251412098239705,-0.018306777944153,0.277837575558899,-0.110473910188767,0.0669280749146731,0.128539358273528,-0.189114843888824,0.133558376740387,-0.0210530534538215,149.62 3 | -------------------------------------------------------------------------------- /credit_card_fraud_detection/requirements.txt: -------------------------------------------------------------------------------- 1 | joblib==1.2.0 2 | numpy==1.23.4 3 | pandas==1.5.1 4 | python-dateutil==2.8.2 5 | pytz==2022.5 6 | scikit-learn==1.1.3 7 | scipy==1.9.3 8 | six==1.16.0 9 | sklearn==0.0 10 | threadpoolctl==3.1.0 11 | -------------------------------------------------------------------------------- /credit_card_fraud_detection/train_model.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | from sklearn.model_selection import train_test_split 3 | from sklearn.ensemble import RandomForestClassifier 4 | from sklearn.metrics import accuracy_score 5 | from joblib import dump, load 6 | 7 | data = pd.read_csv("creditcard.csv") 8 | X = data.drop(['Class'], axis=1) 9 | Y = data["Class"] 10 | X_data = X.values 11 | Y_data = Y.values 12 | X_train, X_test, Y_train, Y_test = train_test_split(X_data, Y_data, test_size = 0.2, random_state = 42) 13 | 14 | 15 | model = RandomForestClassifier() 16 | model.fit(X_train, Y_train) 17 | 18 | y_pred = model.predict(X_test) 19 | print(accuracy_score(Y_test, y_pred)) 20 | 21 | # save model 22 | dump(model, 'model.joblib') 23 | -------------------------------------------------------------------------------- /echo/README.md: -------------------------------------------------------------------------------- 1 | # echo 2 | 3 | A simple function that returns whatever you send it. You can deploy this function by simply running: 4 | ``` 5 | cape deploy echo_deploy 6 | ``` 7 | 8 | Then to invoke it you can run: 9 | ``` 10 | cape run -f input.echo.data 11 | ```` -------------------------------------------------------------------------------- /echo/echo_deploy/app.py: -------------------------------------------------------------------------------- 1 | 2 | def cape_handler(n): 3 | data = n.decode("utf-8") 4 | return data 5 | -------------------------------------------------------------------------------- /echo/input.echo.art: -------------------------------------------------------------------------------- 1 | --- 2 | - -- 3 | --( / \ )XXXXXXXXXXXXX 4 | --XXX( O O )XXXXXXXXXXXXXXX- 5 | /XXX( U ) XXXXXXX\ 6 | /XXXXX( )-- XXXXXXXXXXX\ 7 | /XXXXX/ ( O ) XXXXXX \XXXXX\ 8 | XXXXX/ / XXXXXX \ \XXXXX---- 9 | XXXXXX / XXXXXX \ ---- - 10 | --- XXX / XXXXXX \ --- 11 | -- -- / /\ XXXXXX / ---= 12 | - / XXXXXX '--- XXXXXX 13 | --\/XXX\ XXXXXX /XXXXX 14 | \XXXXXXXXX /XXXXX/ 15 | \XXXXXX /XXXXX/ 16 | \XXXXX-- / -- XXXX/ 17 | --XXXXXXX--------------- XXXXX-- 18 | \XXXXXXXXXXXXXXXXXXXXXXXX- 19 | --XXXXXXXXXXXXXXXXXX- 20 | -------------------------------------------------------------------------------- /echo/input.echo.data: -------------------------------------------------------------------------------- 1 | Hello, World! 2 | -------------------------------------------------------------------------------- /hide_and_seek/README.md: -------------------------------------------------------------------------------- 1 | # Hide and seek 2 | This function is meant to go hand in hand with a [webapp](https://codesandbox.io/s/hide-and-seek-us7feb?file=/src/App.tsx) that hosts 3 | a GUI for interacting with this function. 4 | 5 | ## Function breakdown 6 | The function takes 2 inputs via the semantic connect/invoke. 7 | 8 | One of the inputs is expected to be encrypted while the other one represents 9 | the users' own location. 10 | 11 | It doesn't really matter which location data is passed in first as the function 12 | does not distinguish between the two locations, after receiving two inputs, 13 | the function will return a value. 14 | 15 | 16 | # Setup 17 | Just follow steps from this [blog post](https://dev.to/ericape/hide-and-seek-mnh)! 18 | 19 | -------------------------------------------------------------------------------- /hide_and_seek/requirements.txt: -------------------------------------------------------------------------------- 1 | geographiclib==1.52 2 | geopy==2.2.0 3 | 4 | -------------------------------------------------------------------------------- /hide_and_seek/seek/app.py: -------------------------------------------------------------------------------- 1 | import math 2 | import string 3 | from unicodedata import name 4 | import json 5 | import geopy 6 | from geopy.distance import geodesic as GD 7 | 8 | 9 | FILE_PATH = "./secret.json" 10 | 11 | 12 | def open_secret(path=FILE_PATH): 13 | with open(path) as f: 14 | try: 15 | data = json.load(f) 16 | except Exception: 17 | return None 18 | return data 19 | 20 | 21 | def store_secret(data, path=FILE_PATH): 22 | with open(path, "w", encoding="utf-8") as f: 23 | json.dump(data, f, ensure_ascii=False, indent=4) 24 | 25 | 26 | # Function needs to check if the coordinates match the one that was provided. 27 | def cape_handler(arg): 28 | user_input = json.loads(arg) 29 | 30 | # use file cache instead 31 | secret = open_secret() 32 | if secret == None: 33 | # Store the current input as secret 34 | store_secret(user_input) 35 | else: 36 | # Calculate the difference between the user secret and the current input. 37 | user_loco = (float(user_input["latitude"]), float(user_input["longitude"])) 38 | secret_loco = (float(secret["latitude"]), float(secret["longitude"])) 39 | distance = GD(secret_loco, user_loco).m 40 | if distance < 100: 41 | ret = f"Congrats you found the treasure!" 42 | elif distance < 10000: 43 | ret = f"Getting closer! still {distance} meters away" 44 | else: 45 | ret = f"Not even close! still {distance} meters away" 46 | return ret 47 | -------------------------------------------------------------------------------- /hide_and_seek/seek/secret.json: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/hide_and_seek/seek/secret.json -------------------------------------------------------------------------------- /image_recognition/README.md: -------------------------------------------------------------------------------- 1 | # Image Recognition 2 | 3 | This application performs secure image recognition using a tflite model 4 | 5 | ## Usage 6 | 7 | ### Prepare deployment folder with dependencies 8 | ``` 9 | docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target ./deploy/ 10 | ``` 11 | 12 | ### Deploy and run with Cape 13 | First deploy the model with: 14 | ``` 15 | cape deploy ./deploy 16 | 17 | Deploying function to Cape ... 18 | Function ID ➜ 19 | Function Checksum ➜ 20 | Function Name ➜ 21 | ``` 22 | 23 | Then invoke the model with: 24 | ``` 25 | cape run -f cat.jpg 26 | 27 | ('Image Label is :', 'Egyptian cat', ', with Accuracy :', 72.27, '%.') 28 | ``` 29 | 30 | ``` 31 | cape run UCBjx2Q9j3sQb3f5ywJgZw -f coffee.jpg 32 | 33 | ('Image Label is :', 'espresso', ', with Accuracy :', 84.38, '%.') 34 | ``` 35 | 36 | ``` 37 | cape run UCBjx2Q9j3sQb3f5ywJgZw -f lizzard.jpg 38 | 39 | ('Image Label is :', 'American chameleon', ', with Accuracy :', 69.14, '%.') 40 | ``` -------------------------------------------------------------------------------- /image_recognition/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/image_recognition/cat.jpg -------------------------------------------------------------------------------- /image_recognition/coffee.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/image_recognition/coffee.jpg -------------------------------------------------------------------------------- /image_recognition/deploy/app.py: -------------------------------------------------------------------------------- 1 | from tflite_runtime.interpreter import Interpreter 2 | from PIL import Image 3 | import numpy as np 4 | import time 5 | import io 6 | 7 | 8 | def load_labels(path): # Read the labels from the text file as a Python list. 9 | with open(path, "r") as f: 10 | return [line.strip() for i, line in enumerate(f.readlines())] 11 | 12 | 13 | def set_input_tensor(interpreter, image): 14 | tensor_index = interpreter.get_input_details()[0]["index"] 15 | input_tensor = interpreter.tensor(tensor_index)()[0] 16 | input_tensor[:, :] = image 17 | 18 | 19 | def classify_image(interpreter, image, top_k=1): 20 | set_input_tensor(interpreter, image) 21 | interpreter.invoke() 22 | output_details = interpreter.get_output_details()[0] 23 | output = np.squeeze(interpreter.get_tensor(output_details["index"])) 24 | scale, zero_point = output_details["quantization"] 25 | output = scale * (output - zero_point) 26 | ordered = np.argpartition(-output, 1) 27 | return [(i, output[i]) for i in ordered[:top_k]][0] 28 | 29 | 30 | def cape_handler(image_bytes): 31 | data_folder = "./" 32 | 33 | model_path = data_folder + "model.tflite" 34 | label_path = data_folder + "labels.txt" 35 | 36 | interpreter = Interpreter(model_path) 37 | 38 | interpreter.allocate_tensors() 39 | _, height, width, _ = interpreter.get_input_details()[0]["shape"] 40 | 41 | # Load an image to be classified. 42 | image = Image.open(io.BytesIO(image_bytes)) 43 | image.save(data_folder + "img.jpg") 44 | image = Image.open(data_folder + "img.jpg").convert("RGB").resize((width, height)) 45 | 46 | # Classify the image. 47 | label_id, prob = classify_image(interpreter, image) 48 | 49 | # Read class labels. 50 | labels = load_labels(label_path) 51 | 52 | # Return the classification label of the image. 53 | classification_label = labels[label_id] 54 | return ( 55 | "Image Label is :", 56 | classification_label, 57 | ", with Accuracy :", 58 | np.round(prob * 100, 2), 59 | "%.", 60 | ) 61 | -------------------------------------------------------------------------------- /image_recognition/deploy/labels.txt: -------------------------------------------------------------------------------- 1 | background 2 | tench 3 | goldfish 4 | great white shark 5 | tiger shark 6 | hammerhead 7 | electric ray 8 | stingray 9 | cock 10 | hen 11 | ostrich 12 | brambling 13 | goldfinch 14 | house finch 15 | junco 16 | indigo bunting 17 | robin 18 | bulbul 19 | jay 20 | magpie 21 | chickadee 22 | water ouzel 23 | kite 24 | bald eagle 25 | vulture 26 | great grey owl 27 | European fire salamander 28 | common newt 29 | eft 30 | spotted salamander 31 | axolotl 32 | bullfrog 33 | tree frog 34 | tailed frog 35 | loggerhead 36 | leatherback turtle 37 | mud turtle 38 | terrapin 39 | box turtle 40 | banded gecko 41 | common iguana 42 | American chameleon 43 | whiptail 44 | agama 45 | frilled lizard 46 | alligator lizard 47 | Gila monster 48 | green lizard 49 | African chameleon 50 | Komodo dragon 51 | African crocodile 52 | American alligator 53 | triceratops 54 | thunder snake 55 | ringneck snake 56 | hognose snake 57 | green snake 58 | king snake 59 | garter snake 60 | water snake 61 | vine snake 62 | night snake 63 | boa constrictor 64 | rock python 65 | Indian cobra 66 | green mamba 67 | sea snake 68 | horned viper 69 | diamondback 70 | sidewinder 71 | trilobite 72 | harvestman 73 | scorpion 74 | black and gold garden spider 75 | barn spider 76 | garden spider 77 | black widow 78 | tarantula 79 | wolf spider 80 | tick 81 | centipede 82 | black grouse 83 | ptarmigan 84 | ruffed grouse 85 | prairie chicken 86 | peacock 87 | quail 88 | partridge 89 | African grey 90 | macaw 91 | sulphur-crested cockatoo 92 | lorikeet 93 | coucal 94 | bee eater 95 | hornbill 96 | hummingbird 97 | jacamar 98 | toucan 99 | drake 100 | red-breasted merganser 101 | goose 102 | black swan 103 | tusker 104 | echidna 105 | platypus 106 | wallaby 107 | koala 108 | wombat 109 | jellyfish 110 | sea anemone 111 | brain coral 112 | flatworm 113 | nematode 114 | conch 115 | snail 116 | slug 117 | sea slug 118 | chiton 119 | chambered nautilus 120 | Dungeness crab 121 | rock crab 122 | fiddler crab 123 | king crab 124 | American lobster 125 | spiny lobster 126 | crayfish 127 | hermit crab 128 | isopod 129 | white stork 130 | black stork 131 | spoonbill 132 | flamingo 133 | little blue heron 134 | American egret 135 | bittern 136 | crane 137 | limpkin 138 | European gallinule 139 | American coot 140 | bustard 141 | ruddy turnstone 142 | red-backed sandpiper 143 | redshank 144 | dowitcher 145 | oystercatcher 146 | pelican 147 | king penguin 148 | albatross 149 | grey whale 150 | killer whale 151 | dugong 152 | sea lion 153 | Chihuahua 154 | Japanese spaniel 155 | Maltese dog 156 | Pekinese 157 | Shih-Tzu 158 | Blenheim spaniel 159 | papillon 160 | toy terrier 161 | Rhodesian ridgeback 162 | Afghan hound 163 | basset 164 | beagle 165 | bloodhound 166 | bluetick 167 | black-and-tan coonhound 168 | Walker hound 169 | English foxhound 170 | redbone 171 | borzoi 172 | Irish wolfhound 173 | Italian greyhound 174 | whippet 175 | Ibizan hound 176 | Norwegian elkhound 177 | otterhound 178 | Saluki 179 | Scottish deerhound 180 | Weimaraner 181 | Staffordshire bullterrier 182 | American Staffordshire terrier 183 | Bedlington terrier 184 | Border terrier 185 | Kerry blue terrier 186 | Irish terrier 187 | Norfolk terrier 188 | Norwich terrier 189 | Yorkshire terrier 190 | wire-haired fox terrier 191 | Lakeland terrier 192 | Sealyham terrier 193 | Airedale 194 | cairn 195 | Australian terrier 196 | Dandie Dinmont 197 | Boston bull 198 | miniature schnauzer 199 | giant schnauzer 200 | standard schnauzer 201 | Scotch terrier 202 | Tibetan terrier 203 | silky terrier 204 | soft-coated wheaten terrier 205 | West Highland white terrier 206 | Lhasa 207 | flat-coated retriever 208 | curly-coated retriever 209 | golden retriever 210 | Labrador retriever 211 | Chesapeake Bay retriever 212 | German short-haired pointer 213 | vizsla 214 | English setter 215 | Irish setter 216 | Gordon setter 217 | Brittany spaniel 218 | clumber 219 | English springer 220 | Welsh springer spaniel 221 | cocker spaniel 222 | Sussex spaniel 223 | Irish water spaniel 224 | kuvasz 225 | schipperke 226 | groenendael 227 | malinois 228 | briard 229 | kelpie 230 | komondor 231 | Old English sheepdog 232 | Shetland sheepdog 233 | collie 234 | Border collie 235 | Bouvier des Flandres 236 | Rottweiler 237 | German shepherd 238 | Doberman 239 | miniature pinscher 240 | Greater Swiss Mountain dog 241 | Bernese mountain dog 242 | Appenzeller 243 | EntleBucher 244 | boxer 245 | bull mastiff 246 | Tibetan mastiff 247 | French bulldog 248 | Great Dane 249 | Saint Bernard 250 | Eskimo dog 251 | malamute 252 | Siberian husky 253 | dalmatian 254 | affenpinscher 255 | basenji 256 | pug 257 | Leonberg 258 | Newfoundland 259 | Great Pyrenees 260 | Samoyed 261 | Pomeranian 262 | chow 263 | keeshond 264 | Brabancon griffon 265 | Pembroke 266 | Cardigan 267 | toy poodle 268 | miniature poodle 269 | standard poodle 270 | Mexican hairless 271 | timber wolf 272 | white wolf 273 | red wolf 274 | coyote 275 | dingo 276 | dhole 277 | African hunting dog 278 | hyena 279 | red fox 280 | kit fox 281 | Arctic fox 282 | grey fox 283 | tabby 284 | tiger cat 285 | Persian cat 286 | Siamese cat 287 | Egyptian cat 288 | cougar 289 | lynx 290 | leopard 291 | snow leopard 292 | jaguar 293 | lion 294 | tiger 295 | cheetah 296 | brown bear 297 | American black bear 298 | ice bear 299 | sloth bear 300 | mongoose 301 | meerkat 302 | tiger beetle 303 | ladybug 304 | ground beetle 305 | long-horned beetle 306 | leaf beetle 307 | dung beetle 308 | rhinoceros beetle 309 | weevil 310 | fly 311 | bee 312 | ant 313 | grasshopper 314 | cricket 315 | walking stick 316 | cockroach 317 | mantis 318 | cicada 319 | leafhopper 320 | lacewing 321 | dragonfly 322 | damselfly 323 | admiral 324 | ringlet 325 | monarch 326 | cabbage butterfly 327 | sulphur butterfly 328 | lycaenid 329 | starfish 330 | sea urchin 331 | sea cucumber 332 | wood rabbit 333 | hare 334 | Angora 335 | hamster 336 | porcupine 337 | fox squirrel 338 | marmot 339 | beaver 340 | guinea pig 341 | sorrel 342 | zebra 343 | hog 344 | wild boar 345 | warthog 346 | hippopotamus 347 | ox 348 | water buffalo 349 | bison 350 | ram 351 | bighorn 352 | ibex 353 | hartebeest 354 | impala 355 | gazelle 356 | Arabian camel 357 | llama 358 | weasel 359 | mink 360 | polecat 361 | black-footed ferret 362 | otter 363 | skunk 364 | badger 365 | armadillo 366 | three-toed sloth 367 | orangutan 368 | gorilla 369 | chimpanzee 370 | gibbon 371 | siamang 372 | guenon 373 | patas 374 | baboon 375 | macaque 376 | langur 377 | colobus 378 | proboscis monkey 379 | marmoset 380 | capuchin 381 | howler monkey 382 | titi 383 | spider monkey 384 | squirrel monkey 385 | Madagascar cat 386 | indri 387 | Indian elephant 388 | African elephant 389 | lesser panda 390 | giant panda 391 | barracouta 392 | eel 393 | coho 394 | rock beauty 395 | anemone fish 396 | sturgeon 397 | gar 398 | lionfish 399 | puffer 400 | abacus 401 | abaya 402 | academic gown 403 | accordion 404 | acoustic guitar 405 | aircraft carrier 406 | airliner 407 | airship 408 | altar 409 | ambulance 410 | amphibian 411 | analog clock 412 | apiary 413 | apron 414 | ashcan 415 | assault rifle 416 | backpack 417 | bakery 418 | balance beam 419 | balloon 420 | ballpoint 421 | Band Aid 422 | banjo 423 | bannister 424 | barbell 425 | barber chair 426 | barbershop 427 | barn 428 | barometer 429 | barrel 430 | barrow 431 | baseball 432 | basketball 433 | bassinet 434 | bassoon 435 | bathing cap 436 | bath towel 437 | bathtub 438 | beach wagon 439 | beacon 440 | beaker 441 | bearskin 442 | beer bottle 443 | beer glass 444 | bell cote 445 | bib 446 | bicycle-built-for-two 447 | bikini 448 | binder 449 | binoculars 450 | birdhouse 451 | boathouse 452 | bobsled 453 | bolo tie 454 | bonnet 455 | bookcase 456 | bookshop 457 | bottlecap 458 | bow 459 | bow tie 460 | brass 461 | brassiere 462 | breakwater 463 | breastplate 464 | broom 465 | bucket 466 | buckle 467 | bulletproof vest 468 | bullet train 469 | butcher shop 470 | cab 471 | caldron 472 | candle 473 | cannon 474 | canoe 475 | can opener 476 | cardigan 477 | car mirror 478 | carousel 479 | carpenter's kit 480 | carton 481 | car wheel 482 | cash machine 483 | cassette 484 | cassette player 485 | castle 486 | catamaran 487 | CD player 488 | cello 489 | cellular telephone 490 | chain 491 | chainlink fence 492 | chain mail 493 | chain saw 494 | chest 495 | chiffonier 496 | chime 497 | china cabinet 498 | Christmas stocking 499 | church 500 | cinema 501 | cleaver 502 | cliff dwelling 503 | cloak 504 | clog 505 | cocktail shaker 506 | coffee mug 507 | coffeepot 508 | coil 509 | combination lock 510 | computer keyboard 511 | confectionery 512 | container ship 513 | convertible 514 | corkscrew 515 | cornet 516 | cowboy boot 517 | cowboy hat 518 | cradle 519 | crane 520 | crash helmet 521 | crate 522 | crib 523 | Crock Pot 524 | croquet ball 525 | crutch 526 | cuirass 527 | dam 528 | desk 529 | desktop computer 530 | dial telephone 531 | diaper 532 | digital clock 533 | digital watch 534 | dining table 535 | dishrag 536 | dishwasher 537 | disk brake 538 | dock 539 | dogsled 540 | dome 541 | doormat 542 | drilling platform 543 | drum 544 | drumstick 545 | dumbbell 546 | Dutch oven 547 | electric fan 548 | electric guitar 549 | electric locomotive 550 | entertainment center 551 | envelope 552 | espresso maker 553 | face powder 554 | feather boa 555 | file 556 | fireboat 557 | fire engine 558 | fire screen 559 | flagpole 560 | flute 561 | folding chair 562 | football helmet 563 | forklift 564 | fountain 565 | fountain pen 566 | four-poster 567 | freight car 568 | French horn 569 | frying pan 570 | fur coat 571 | garbage truck 572 | gasmask 573 | gas pump 574 | goblet 575 | go-kart 576 | golf ball 577 | golfcart 578 | gondola 579 | gong 580 | gown 581 | grand piano 582 | greenhouse 583 | grille 584 | grocery store 585 | guillotine 586 | hair slide 587 | hair spray 588 | half track 589 | hammer 590 | hamper 591 | hand blower 592 | hand-held computer 593 | handkerchief 594 | hard disc 595 | harmonica 596 | harp 597 | harvester 598 | hatchet 599 | holster 600 | home theater 601 | honeycomb 602 | hook 603 | hoopskirt 604 | horizontal bar 605 | horse cart 606 | hourglass 607 | iPod 608 | iron 609 | jack-o'-lantern 610 | jean 611 | jeep 612 | jersey 613 | jigsaw puzzle 614 | jinrikisha 615 | joystick 616 | kimono 617 | knee pad 618 | knot 619 | lab coat 620 | ladle 621 | lampshade 622 | laptop 623 | lawn mower 624 | lens cap 625 | letter opener 626 | library 627 | lifeboat 628 | lighter 629 | limousine 630 | liner 631 | lipstick 632 | Loafer 633 | lotion 634 | loudspeaker 635 | loupe 636 | lumbermill 637 | magnetic compass 638 | mailbag 639 | mailbox 640 | maillot 641 | maillot 642 | manhole cover 643 | maraca 644 | marimba 645 | mask 646 | matchstick 647 | maypole 648 | maze 649 | measuring cup 650 | medicine chest 651 | megalith 652 | microphone 653 | microwave 654 | military uniform 655 | milk can 656 | minibus 657 | miniskirt 658 | minivan 659 | missile 660 | mitten 661 | mixing bowl 662 | mobile home 663 | Model T 664 | modem 665 | monastery 666 | monitor 667 | moped 668 | mortar 669 | mortarboard 670 | mosque 671 | mosquito net 672 | motor scooter 673 | mountain bike 674 | mountain tent 675 | mouse 676 | mousetrap 677 | moving van 678 | muzzle 679 | nail 680 | neck brace 681 | necklace 682 | nipple 683 | notebook 684 | obelisk 685 | oboe 686 | ocarina 687 | odometer 688 | oil filter 689 | organ 690 | oscilloscope 691 | overskirt 692 | oxcart 693 | oxygen mask 694 | packet 695 | paddle 696 | paddlewheel 697 | padlock 698 | paintbrush 699 | pajama 700 | palace 701 | panpipe 702 | paper towel 703 | parachute 704 | parallel bars 705 | park bench 706 | parking meter 707 | passenger car 708 | patio 709 | pay-phone 710 | pedestal 711 | pencil box 712 | pencil sharpener 713 | perfume 714 | Petri dish 715 | photocopier 716 | pick 717 | pickelhaube 718 | picket fence 719 | pickup 720 | pier 721 | piggy bank 722 | pill bottle 723 | pillow 724 | ping-pong ball 725 | pinwheel 726 | pirate 727 | pitcher 728 | plane 729 | planetarium 730 | plastic bag 731 | plate rack 732 | plow 733 | plunger 734 | Polaroid camera 735 | pole 736 | police van 737 | poncho 738 | pool table 739 | pop bottle 740 | pot 741 | potter's wheel 742 | power drill 743 | prayer rug 744 | printer 745 | prison 746 | projectile 747 | projector 748 | puck 749 | punching bag 750 | purse 751 | quill 752 | quilt 753 | racer 754 | racket 755 | radiator 756 | radio 757 | radio telescope 758 | rain barrel 759 | recreational vehicle 760 | reel 761 | reflex camera 762 | refrigerator 763 | remote control 764 | restaurant 765 | revolver 766 | rifle 767 | rocking chair 768 | rotisserie 769 | rubber eraser 770 | rugby ball 771 | rule 772 | running shoe 773 | safe 774 | safety pin 775 | saltshaker 776 | sandal 777 | sarong 778 | sax 779 | scabbard 780 | scale 781 | school bus 782 | schooner 783 | scoreboard 784 | screen 785 | screw 786 | screwdriver 787 | seat belt 788 | sewing machine 789 | shield 790 | shoe shop 791 | shoji 792 | shopping basket 793 | shopping cart 794 | shovel 795 | shower cap 796 | shower curtain 797 | ski 798 | ski mask 799 | sleeping bag 800 | slide rule 801 | sliding door 802 | slot 803 | snorkel 804 | snowmobile 805 | snowplow 806 | soap dispenser 807 | soccer ball 808 | sock 809 | solar dish 810 | sombrero 811 | soup bowl 812 | space bar 813 | space heater 814 | space shuttle 815 | spatula 816 | speedboat 817 | spider web 818 | spindle 819 | sports car 820 | spotlight 821 | stage 822 | steam locomotive 823 | steel arch bridge 824 | steel drum 825 | stethoscope 826 | stole 827 | stone wall 828 | stopwatch 829 | stove 830 | strainer 831 | streetcar 832 | stretcher 833 | studio couch 834 | stupa 835 | submarine 836 | suit 837 | sundial 838 | sunglass 839 | sunglasses 840 | sunscreen 841 | suspension bridge 842 | swab 843 | sweatshirt 844 | swimming trunks 845 | swing 846 | switch 847 | syringe 848 | table lamp 849 | tank 850 | tape player 851 | teapot 852 | teddy 853 | television 854 | tennis ball 855 | thatch 856 | theater curtain 857 | thimble 858 | thresher 859 | throne 860 | tile roof 861 | toaster 862 | tobacco shop 863 | toilet seat 864 | torch 865 | totem pole 866 | tow truck 867 | toyshop 868 | tractor 869 | trailer truck 870 | tray 871 | trench coat 872 | tricycle 873 | trimaran 874 | tripod 875 | triumphal arch 876 | trolleybus 877 | trombone 878 | tub 879 | turnstile 880 | typewriter keyboard 881 | umbrella 882 | unicycle 883 | upright 884 | vacuum 885 | vase 886 | vault 887 | velvet 888 | vending machine 889 | vestment 890 | viaduct 891 | violin 892 | volleyball 893 | waffle iron 894 | wall clock 895 | wallet 896 | wardrobe 897 | warplane 898 | washbasin 899 | washer 900 | water bottle 901 | water jug 902 | water tower 903 | whiskey jug 904 | whistle 905 | wig 906 | window screen 907 | window shade 908 | Windsor tie 909 | wine bottle 910 | wing 911 | wok 912 | wooden spoon 913 | wool 914 | worm fence 915 | wreck 916 | yawl 917 | yurt 918 | web site 919 | comic book 920 | crossword puzzle 921 | street sign 922 | traffic light 923 | book jacket 924 | menu 925 | plate 926 | guacamole 927 | consomme 928 | hot pot 929 | trifle 930 | ice cream 931 | ice lolly 932 | French loaf 933 | bagel 934 | pretzel 935 | cheeseburger 936 | hotdog 937 | mashed potato 938 | head cabbage 939 | broccoli 940 | cauliflower 941 | zucchini 942 | spaghetti squash 943 | acorn squash 944 | butternut squash 945 | cucumber 946 | artichoke 947 | bell pepper 948 | cardoon 949 | mushroom 950 | Granny Smith 951 | strawberry 952 | orange 953 | lemon 954 | fig 955 | pineapple 956 | banana 957 | jackfruit 958 | custard apple 959 | pomegranate 960 | hay 961 | carbonara 962 | chocolate sauce 963 | dough 964 | meat loaf 965 | pizza 966 | potpie 967 | burrito 968 | red wine 969 | espresso 970 | cup 971 | eggnog 972 | alp 973 | bubble 974 | cliff 975 | coral reef 976 | geyser 977 | lakeside 978 | promontory 979 | sandbar 980 | seashore 981 | valley 982 | volcano 983 | ballplayer 984 | groom 985 | scuba diver 986 | rapeseed 987 | daisy 988 | yellow lady's slipper 989 | corn 990 | acorn 991 | hip 992 | buckeye 993 | coral fungus 994 | agaric 995 | gyromitra 996 | stinkhorn 997 | earthstar 998 | hen-of-the-woods 999 | bolete 1000 | ear 1001 | toilet tissue 1002 | -------------------------------------------------------------------------------- /image_recognition/deploy/model.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/image_recognition/deploy/model.tflite -------------------------------------------------------------------------------- /image_recognition/lizzard.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/image_recognition/lizzard.jpg -------------------------------------------------------------------------------- /image_recognition/requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.23.2 2 | Pillow==9.2.0 3 | tflite-runtime==2.10.0 4 | -------------------------------------------------------------------------------- /isprime/README.md: -------------------------------------------------------------------------------- 1 | # isprime 2 | 3 | A simple function checking if your number is prime or not. You can deploy this function by simply running: 4 | ``` 5 | cape deploy isprime_deploy 6 | ``` 7 | 8 | Then to invoke it you can run: 9 | ``` 10 | cape run -f input.isprime.data 11 | ```` -------------------------------------------------------------------------------- /isprime/input.isprime.data: -------------------------------------------------------------------------------- 1 | 7 2 | -------------------------------------------------------------------------------- /isprime/isprime_deploy/app.py: -------------------------------------------------------------------------------- 1 | import math 2 | 3 | def isprime(n): 4 | if n < 2: 5 | return False 6 | if n == 2: 7 | return True 8 | for i in range(2, int(math.sqrt(n))): 9 | if n % i == 0: 10 | return False 11 | return True 12 | 13 | def cape_handler(arg): 14 | n = int(arg) 15 | result = isprime(n) 16 | if result: 17 | ret = f"{n} is prime" 18 | else: 19 | ret = f"{n} is NOT prime" 20 | return ret 21 | -------------------------------------------------------------------------------- /isprime/isprime_js_deploy/app.js: -------------------------------------------------------------------------------- 1 | const isPrime = (number) => { 2 | if (number <= 1) { 3 | return false; 4 | } 5 | for (let divisor = 2; divisor <= Math.sqrt(number); divisor++) { 6 | if (number % divisor === 0) { 7 | return false; 8 | } 9 | } 10 | return true; 11 | } 12 | 13 | const capeHandler = (data) => { 14 | const n = parseInt(data); 15 | const result = isPrime(n); 16 | let ret; 17 | if (result) { 18 | ret = `${n} is prime` 19 | } else { 20 | ret = `${n} is NOT prime` 21 | } 22 | return ret; 23 | }; 24 | 25 | module.exports = { 26 | capeHandler 27 | }; 28 | -------------------------------------------------------------------------------- /leader-election/README.md: -------------------------------------------------------------------------------- 1 | # Leader election using enclaves 2 | The premise of this repo is to set up a network of python nodes that will ask the enclave to run the leader election algorithm for them. 3 | 4 | # Setup 5 | ## Fetch dependencies for function 6 | Fetch depdencies before deploy: 7 | ``` 8 | pip install -r function/requirements.txt --target function/ 9 | ``` 10 | 11 | You would first have to deploy the function: 12 | ``` 13 | cape login // then follow prompt 14 | cape deploy function/ 15 | cape token --function-checksum -o json > leader_election.json // with output from `deploy` step 16 | ``` 17 | 18 | Then update the function hash and function id with when starting up the nodes. 19 | 20 | Install crypto package for key pair generation 21 | ``` 22 | pip install -r requirements.txt 23 | ``` 24 | 25 | Generate the crypto content (in python) and run: 26 | ``` python 27 | from Crypto.PublicKey import RSA 28 | key = RSA.generate(2048) 29 | private_key_bytes = key.export_key('PEM') 30 | public_key_bytes = key.publickey().exportKey('PEM') 31 | 32 | 33 | with open("private.pem", "wb") as f: 34 | f.write(private_key_bytes) 35 | with open("public.pem", "wb") as f: 36 | f.write(public_key_bytes) 37 | ``` 38 | Begin a list of python nodes which are identified by IP and port number. 39 | ``` 40 | python start_node.py -p 5000 -n 5001 41 | python start_node.py -p 5001 -n 5000 42 | ``` 43 | 44 | # The algorithm 45 | Typically leader election requires nodes to behave properly when transmitting messages so that each node 46 | executes the proper logic. Instead of relying on each node deciding who the leader is, we now leverage 47 | the enclave for this gurantee. 48 | 49 | A simple example would be for `node A` to gossip and update the list of active nodes it knows. 50 | It would send this data encrypted with the enclave's public key to make sure only the enclave can decrypt the message (not implemented) 51 | Next the enclave would sign the result and would return both the signature and orginal value. 52 | 53 | This new value is now trusted to be true given the list of nodes available. 54 | This gurantees that if the list of active nodes matches, then the leader is deterministic. 55 | 56 | ## Byzantine fault tolerance 57 | What if a node sends an arbitrary list of nodes? 58 | All good behaving nodes would know that the list of nodes in the signed result doesn't match the list of 59 | active nodes, and would not respect the leader elected in that case. 60 | 61 | Health checks, network coalescence are omitted from the current code for simplicity purposes. 62 | 63 | The current example does not satisfy Byzantine fault tolerance. To do that we would need to collect 64 | the signed views of each of the nodes in order to be able to gurantee that the leader elected is correct. 65 | This would be an interesting idea to explore based on the current implementation. 66 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /leader-election/function/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | from unicodedata import name 3 | 4 | from Crypto import Random 5 | from Crypto.Cipher import PKCS1_OAEP 6 | from Crypto.Hash import SHA256 7 | from Crypto.PublicKey import RSA 8 | from Crypto.Signature import PKCS1_v1_5 9 | 10 | 11 | # Function needs to check if the coordinates match the one that was provided. 12 | def cape_handler(arg): 13 | user_input = json.loads(arg) 14 | 15 | if "node_list" not in user_input: 16 | raise Exception("node list is missing") 17 | 18 | nodes = user_input.get("node_list") 19 | 20 | nodes.sort() 21 | 22 | message = str(nodes[-1]) 23 | digest = SHA256.new() 24 | digest.update(message.encode("utf-8")) 25 | print("digest", digest) 26 | 27 | private_key = RSA.importKey(open("private.pem").read()) 28 | signer = PKCS1_v1_5.new(private_key) 29 | sig = signer.sign(digest) 30 | return_val = {} 31 | return_val["message"] = message 32 | return_val["signature"] = sig.hex() 33 | print("digest in hex", sig.hex()) 34 | return json.dumps(return_val) 35 | -------------------------------------------------------------------------------- /leader-election/function/requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome==3.15 2 | -------------------------------------------------------------------------------- /leader-election/gen_rsa_kay_pair.py: -------------------------------------------------------------------------------- 1 | from Crypto.PublicKey import RSA 2 | 3 | key = RSA.generate(2048) 4 | private_key_bytes = key.export_key('PEM') 5 | public_key_bytes = key.publickey().exportKey('PEM') 6 | 7 | 8 | with open("private.pem", "wb") as f: 9 | f.write(private_key_bytes) 10 | with open("public.pem", "wb") as f: 11 | f.write(public_key_bytes) 12 | -------------------------------------------------------------------------------- /leader-election/gossip/__init__.py: -------------------------------------------------------------------------------- 1 | from gossip.gossip_protocol import GossipNode 2 | 3 | __all__ = [ 4 | GossipNode, 5 | ] 6 | -------------------------------------------------------------------------------- /leader-election/gossip/gossip_protocol.py: -------------------------------------------------------------------------------- 1 | import json 2 | import random 3 | import socket 4 | import time 5 | from threading import Thread 6 | 7 | from Crypto.Hash import SHA256 8 | from Crypto.PublicKey import RSA 9 | from Crypto.Signature import PKCS1_v1_5 10 | from pycape import Cape 11 | 12 | 13 | class GossipNode: 14 | def __init__(self, port, connected_nodes, function_ref): 15 | # create a new socket instance 16 | # use SOCK_DGRAM to be able to send data without a connection 17 | # being established (connectionless protocol) 18 | self.node = socket.socket(type=socket.SOCK_DGRAM) 19 | 20 | # set the address, i.e(hostname and port) of the socket 21 | self.hostname = socket.gethostname() 22 | self.port = port 23 | self.function_ref = function_ref 24 | # bind the address to the socket created 25 | self.node.bind((self.hostname, self.port)) 26 | 27 | # set the ports of the nodes connected to it as connected nodes 28 | self.connected_nodes = connected_nodes 29 | self.all_nodes = [self.port] 30 | 31 | print("Node started on port {0}".format(self.port)) 32 | print("Connected nodes =>", self.connected_nodes) 33 | # expect the public certificate is located under function folder 34 | public_key = RSA.importKey(open("function/public.pem").read()) 35 | verifier = PKCS1_v1_5.new(public_key) 36 | 37 | self.verifier = verifier 38 | self.client = Cape() 39 | self.start_threads() 40 | 41 | def input_message(self): 42 | while True: 43 | message_dict = {} 44 | message_dict["node_list"] = self.all_nodes 45 | message_to_send = json.dumps(message_dict) 46 | print(message_to_send) 47 | user_data = bytes(message_to_send, "utf8") 48 | result = self.client.run( 49 | self.function_ref, 50 | user_data, 51 | ) 52 | leader = json.loads(result) 53 | message = leader["message"].encode("utf-8") 54 | signature = bytes.fromhex(leader["signature"]) 55 | digest = SHA256.new() 56 | digest.update(message) 57 | verify = self.verifier.verify(digest, signature) 58 | if verify: 59 | print("Successfully verified message") 60 | else: 61 | print("FAILED") 62 | 63 | if leader["message"] == str(self.port): 64 | print("I AM THE LEADER") 65 | 66 | # call send message method and pass the input message. 67 | # encode the message into ascii 68 | send_to = self.connected_nodes.copy() 69 | self.transmit_message(message_to_send.encode("ascii"), send_to) 70 | time.sleep(20) 71 | 72 | def receive_message(self): 73 | while True: 74 | # since we are using connectionless protocol, 75 | # we will use 'recvfrom' to receive UDP message 76 | message_to_forward, address = self.node.recvfrom(1024) 77 | nodes = json.loads(message_to_forward) 78 | 79 | for i in nodes["node_list"]: 80 | if i not in self.all_nodes: 81 | self.all_nodes.append(i) 82 | # remove the port(node), from which the message came from, 83 | to_send = self.connected_nodes.copy() 84 | 85 | print("received from", address[1]) 86 | if address[1] in to_send: 87 | print("poisoned sender") 88 | to_send.remove(address[1]) 89 | # GossipNode.infected_nodes.append(address[1]) 90 | 91 | # sleep for 2 seconds in order to show difference in time 92 | time.sleep(2) 93 | 94 | # print message with the current time. 95 | # decode message so as to print it, as it was sent 96 | print( 97 | "\nMessage is: '{0}'.\nReceived at [{1}] from [{2}]\n".format( 98 | message_to_forward.decode("ascii"), 99 | time.ctime(time.time()), 100 | address[1], 101 | ) 102 | ) 103 | 104 | # call send message to forward the message to other connected nodes 105 | self.transmit_message(message_to_forward, to_send) 106 | 107 | def transmit_message(self, message, to_send): 108 | print("sending to", to_send) 109 | # loop as long as there are connected nodes to send to 110 | while len(to_send) > 0: 111 | # select a random port from the list of connected nodes 112 | selected_port = random.choice(to_send) 113 | print("Connected nodes =>", to_send) 114 | print("Port selected is [{0}]".format(selected_port)) 115 | 116 | # since we are using connectionless protocol, 117 | # we will use 'sendto' to transmit the UDP message 118 | self.node.sendto(message, (self.hostname, selected_port)) 119 | to_send.remove(selected_port) 120 | 121 | print( 122 | "Message: '{0}' sent to [{1}].".format( 123 | message.decode("ascii"), selected_port 124 | ) 125 | ) 126 | print("Connected nodes =>", self.connected_nodes) 127 | print("-" * 50) 128 | time.sleep(2) 129 | print("\n") 130 | 131 | def start_threads(self): 132 | # two threads for sending a gossip and receiving a gossip. 133 | Thread(target=self.input_message).start() 134 | Thread(target=self.receive_message).start() 135 | -------------------------------------------------------------------------------- /leader-election/requirements.txt: -------------------------------------------------------------------------------- 1 | pycryptodome==3.15 2 | -------------------------------------------------------------------------------- /leader-election/start_node.py: -------------------------------------------------------------------------------- 1 | import os 2 | import pathlib 3 | from argparse import ArgumentParser 4 | 5 | from gossip import GossipNode 6 | from pycape import FunctionRef 7 | 8 | # port = 5000 9 | # # ports for the nodes connected to this node 10 | # connected_nodes = [5010, 5020] 11 | 12 | # node = GossipNode(port, connected_nodes) 13 | 14 | if __name__ == "__main__": 15 | parser = ArgumentParser() 16 | parser.add_argument("-p", type=int, help="port") 17 | parser.add_argument("-n", "--node-list", nargs="+", default=[]) 18 | args = vars(parser.parse_args()) 19 | # print(args) 20 | function_json = os.environ.get("FUNCTION_JSON", "leader_election.json") 21 | function_json = pathlib.Path(__file__).parent.absolute() / function_json 22 | 23 | function_ref = FunctionRef.from_json(function_json) 24 | node = GossipNode( 25 | args["p"], 26 | connected_nodes=list(map(int, args["node_list"])), 27 | function_ref=function_ref, 28 | ) 29 | -------------------------------------------------------------------------------- /logistic_regression_sklearn/README.md: -------------------------------------------------------------------------------- 1 | # Logistic Regression Training with Sklearn 2 | 3 | This application securely trains and an Sklearn logistic regression model on the breast cancer dataset available at: https://archive.ics.uci.edu/ml/datasets/breast+cancer that predicts if a tumor is benign or malignant. 4 | 5 | ## Usage 6 | 7 | ### Prepare deployment folder with dependencies 8 | ``` 9 | sudo docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target ./app/ 10 | ``` 11 | 12 | ### Deploy and run with Cape 13 | ``` 14 | cape login 15 | 16 | Your CLI confirmation code is: GZPN-KHMT 17 | Visit this URL to complete the login process: https://login.capeprivacy.com/activate?user_code=GZPN-KHMT 18 | Congratulations, you're all set! 19 | ``` 20 | 21 | ``` 22 | cape deploy ./app 23 | 24 | Deploying function to Cape ... 25 | Success! Deployed function to Cape. 26 | Function ID ➜ aMpFzfsfzM7CUtAtRyiKLA 27 | Function Checksum ➜ a6b1a4e72804ad22d821bb7c89b4647c50920b8a3a4c7e8ecf14b05972215e4d 28 | ``` 29 | 30 | Train logistic regression model on the breast cancer dataset. 31 | ``` 32 | cape run aMpFzfsfzM7CUtAtRyiKLA -f breast_cancer_data.csv --insecure -u https://k8s-cape-enclaver-750003af11-e3080498c852b366.elb.us-east-1.amazonaws.com 33 | 34 | {'accuracy': 0.983957219251337, 'weights': [[-0.5634828454424664, -0.48968117436692676, -0.5277950966176833, -0.5814241972963736, -0.02288592679744086, 0.463860632669327, -0.8282900462209646, -0.8435707325454246, -0.03884918433068487, 0.6187809658667796, -1.0455764381611419, 0.35974835699506585, -0.6170693739746697, -0.9668590026888855, -0.3292909166850597, 0.6467698083688733, 0.09042092086623944, -0.35750055003409836, 0.27136532243728123, 0.6701363867829073, -0.8061574359542534, -1.3329147207091345, -0.654430417620746, -0.8479339690147301, -1.0087746783757932, 0.05189492319476721, -0.8625911548971675, -1.0343961479743573, -1.0175950492834187, -0.08501942035320843]], 'bias': [0.20440503950068414]} 35 | ``` 36 | -------------------------------------------------------------------------------- /logistic_regression_sklearn/app/app.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import pandas as pd 3 | from sklearn.linear_model import LogisticRegression 4 | from sklearn.pipeline import make_pipeline 5 | from sklearn.preprocessing import StandardScaler 6 | 7 | 8 | def cape_handler(input_data): 9 | csv = input_data.decode("utf-8") 10 | csv = csv.replace("\\t", ",").replace("\\n", "\n") 11 | f = open("data.csv", "w") 12 | f.write(csv) 13 | f.close() 14 | 15 | data = pd.read_csv("data.csv") 16 | # data = pd.read_csv('../breast_cancer_data.csv') 17 | data_size = data.shape[0] 18 | test_split = 0.33 19 | test_size = int(data_size * test_split) 20 | 21 | choices = np.arange(0, data_size) 22 | test = np.random.choice(choices, test_size, replace=False) 23 | train = np.delete(choices, test) 24 | 25 | test_set = data.iloc[test] 26 | train_set = data.iloc[train] 27 | 28 | column_names = list(data.columns.values) 29 | features = column_names[1 : len(column_names) - 1] 30 | 31 | y_train = train_set["target"] 32 | y_test = test_set["target"] 33 | X_train = train_set[features] 34 | X_test = test_set[features] 35 | 36 | lr = make_pipeline(StandardScaler(), LogisticRegression()) 37 | lr.fit(X_train, y_train) 38 | pred = lr.predict(X_test) 39 | 40 | accuracy = sum(pred == y_test) / pred.shape[0] 41 | 42 | # trained model 43 | model = { 44 | "accuracy": accuracy, 45 | "weights": lr[1].coef_.tolist(), 46 | "bias": lr[1].intercept_.tolist(), 47 | } 48 | 49 | return model -------------------------------------------------------------------------------- /logistic_regression_sklearn/requirements.txt: -------------------------------------------------------------------------------- 1 | joblib==1.2.0 2 | numpy==1.23.4 3 | pandas==1.5.0 4 | python-dateutil==2.8.2 5 | pytz==2022.5 6 | scikit-learn==1.1.2 7 | scipy==1.9.2 8 | six==1.16.0 9 | sklearn==0.0 10 | threadpoolctl==3.1.0 11 | -------------------------------------------------------------------------------- /mortgage/README.md: -------------------------------------------------------------------------------- 1 | # Mortgage Calculator 2 | 3 | This application is a mortgage calculator that computes if an applicant is eligible for a mortgage 4 | ## Usage 5 | 6 | ### Deploy and run with Cape 7 | ``` 8 | cape login 9 | 10 | Your CLI confirmation code is: PTHZ-WJFJ 11 | Visit this URL to complete the login process: https://login.capeprivacy.com/activate?user_code=PTHZ-WJFJ 12 | Congratulations, you're all set! 13 | ``` 14 | 15 | ``` 16 | cape deploy ./app 17 | 18 | Deploying function to Cape ... 19 | Function ID ➜ 20 | Function Checksum ➜ 21 | Function Name ➜ 22 | ``` 23 | 24 | ``` 25 | cape run -f input.mortgage.json 26 | 27 | Congratulations! You qualify for a mortgage. 28 | ``` -------------------------------------------------------------------------------- /mortgage/app/app.py: -------------------------------------------------------------------------------- 1 | # Determine if someone is eligible for a mortgage 2 | import json 3 | 4 | MIN_DOWN_PERCENTAGE = 0.05 5 | MIN_SALARY = 0.1 6 | 7 | def mortgage(salary, amount, down): 8 | downPercentage = (100 * down) / amount 9 | salaryPercentage = (100 * salary) / amount 10 | if down > amount: 11 | return "You do not require a mortgage since you can put down more than the mortgage amount" 12 | if downPercentage < MIN_DOWN_PERCENTAGE: 13 | return "You do not qualify for a mortgage. You need a down payment of at least 5 percent of the house cost." 14 | if salaryPercentage < MIN_SALARY: 15 | return "You do not qualify for a mortgage. You need a salary of at least 10 percent of the house cost." 16 | return "Congratulations! You qualify for a mortgage." 17 | 18 | def cape_handler(input_data): 19 | input_data = json.loads(input_data) 20 | salary = input_data["salary"] 21 | amount = input_data["amount"] 22 | down = input_data["down"] 23 | return mortgage(salary, amount, down) 24 | -------------------------------------------------------------------------------- /mortgage/input.mortgage.json: -------------------------------------------------------------------------------- 1 | { "salary": 100000, "amount": 500000, "down": 100000 } 2 | -------------------------------------------------------------------------------- /np-stats/README.md: -------------------------------------------------------------------------------- 1 | ### np-stats 2 | 3 | A simple example with numpy dependencies and using [pycape](https://github.com/capeprivacy/pycape) and [serdio.lift_io](https://pydocs.capeprivacy.com/serdio.io_lifter.html#serdio.io_lifter.lift_io) to handle automatic serialization/deserialization of Cape function input/outputs. All commands are run from the repo root directory. 4 | 5 | Note the `numpy_serde.py` helper, which defines a custom encoder/decoder bundle that allows `serdio` to handle numpy arrays. 6 | 7 | **Build the deployment package:** 8 | 9 | ```bash 10 | # Create a deployment folder 11 | mkdir np-stats-deployment 12 | # Copy the cape function (app.py) in the deployment folder 13 | cp app.py numpy_serde.py np-stats-deployment/. 14 | # Add serdio and numpy dependencies using docker 15 | docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target np-stats-deployment/ 16 | ``` 17 | 18 | **Deploy the function:** 19 | 20 | Deploy with the CLI the function as follow: 21 | ``` 22 | cape deploy np-stats-deployment 23 | export FUNCTION_ID= 24 | ``` 25 | Generate the function token based on the function ID and function checksum returned by deploy: 26 | ``` 27 | cape token create --name np-stats 28 | export TOKEN= 29 | ``` 30 | 31 | **Run the function:** 32 | You can run the function with PyCape as follow: 33 | ``` 34 | python run.py 35 | ``` -------------------------------------------------------------------------------- /np-stats/app.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | 3 | import serdio 4 | 5 | import numpy_serde 6 | 7 | 8 | @serdio.lift_io( 9 | hook_bundle=(numpy_serde.encoder, numpy_serde.decoder), 10 | as_handler=True, 11 | ) 12 | def cape_handler(x: np.ndarray) -> np.ndarray: 13 | return x + np.ones(4) 14 | -------------------------------------------------------------------------------- /np-stats/numpy_serde.py: -------------------------------------------------------------------------------- 1 | import msgpack 2 | import numpy as np 3 | 4 | 5 | def encoder(x): 6 | if isinstance(x, np.ndarray): 7 | return {"__type__": "ndarray", "ndarray_bytes": _ndarray_to_bytes(x)} 8 | elif np.issctype(type(x)): 9 | # pack scalar as ndarray 10 | return { 11 | "__type__": "npscalar", 12 | "npscalar_bytes": _ndarray_to_bytes(np.asarray(x)), 13 | } 14 | 15 | 16 | def decoder(ddict): 17 | if "__type__" in ddict: 18 | if ddict["__type__"] == "ndarray": 19 | return _ndarray_from_bytes(ddict["ndarray_bytes"]) 20 | elif ddict["__type__"] == "npscalar": 21 | arr = _ndarray_from_bytes(ddict["npscalar_bytes"]) 22 | return arr[()] 23 | return ddict 24 | 25 | 26 | def _ndarray_to_bytes(arr) -> bytes: 27 | """Save ndarray to simple msgpack encoding.""" 28 | if arr.dtype.hasobject or arr.dtype.isalignedstruct: 29 | raise ValueError( 30 | "Object and structured dtypes not supported " 31 | "for serialization of ndarrays." 32 | ) 33 | tpl = (arr.shape, arr.dtype.name, arr.tobytes("C")) 34 | return msgpack.packb(tpl, use_bin_type=True) 35 | 36 | 37 | def _ndarray_from_bytes(data: bytes) -> np.ndarray: 38 | """Load ndarray from simple msgpack encoding.""" 39 | shape, dtype_name, buffer = msgpack.unpackb(data, raw=True) 40 | return np.frombuffer( 41 | buffer, dtype=np.dtype(dtype_name), count=-1, offset=0 42 | ).reshape(shape, order="C") 43 | -------------------------------------------------------------------------------- /np-stats/numpy_token.json: -------------------------------------------------------------------------------- 1 | {"function_id":"V8cwC4VZAbpwkzcVsCYmdk","function_token":"eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjM1MjkyNjg3MjcsImlhdCI6MTY2NDU1NjY3OCwiaXNzIjoiZ2l0aHVifDgwMDEwMTYiLCJzY29wZSI6ImZ1bmN0aW9uOmludm9rZSIsInN1YiI6IlY4Y3dDNFZaQWJwd2t6Y1ZzQ1ltZGsifQ.bVfSOs3wQ4W-_DrqyT7xSeholOWKdZi7acpZonMmehm_i5D4EOn2_Qt6t8XS87j3--Rczd1IBibXT7T8Y4F3CeCuLpZTjZgmvumuvB4BR9nkDxQCHbzqOKP0063SwwwDFah5lkJSQQNsttXoNevMUsLLJrVqsOQM94FbZIGLusjwpJLqtuydJnX-tpbor_31PiJOoXe24d4Hvmz-5O0vRbOuQMyOxQ4hW6T0sF3Z8UlTUWOayUA6vHo_1hQcHfch958aI3DYGIv1Ut-UYO-O0XKBr_t9J-LIbY0qCkIiW9uqoWjmpDXu9OwGHIPRWSp1NYE0NmYnNPfffG1uTVXFog","function_checksum":"e2bb2ead1a4082fa390aad487cfe472f8a5f7e4d19e11bffe3452995bbf56136"} 2 | -------------------------------------------------------------------------------- /np-stats/requirements.txt: -------------------------------------------------------------------------------- 1 | serdio==1.0.0 2 | numpy==1.23.2 -------------------------------------------------------------------------------- /np-stats/run.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | import numpy as np 4 | from pycape import Cape 5 | 6 | 7 | import numpy_serde as serde 8 | 9 | if __name__ == "__main__": 10 | function_id_env = os.environ.get("FUNCTION_ID") 11 | token_env = os.environ.get("TOKEN") 12 | 13 | cape = Cape() 14 | f = cape.function(function_id_env) 15 | t = cape.token(token_env) 16 | 17 | x = np.array([1, 2, 3, 4]) 18 | result = cape.run(f, t, x, serde_hooks=(serde.encoder, serde.decoder), use_serdio=True) 19 | 20 | print(f"The result is: {result}") 21 | -------------------------------------------------------------------------------- /ocr/README.md: -------------------------------------------------------------------------------- 1 | # Invoke Cape's Confidential OCR Service from SDKs 2 | 3 | This folder shows you how you can run the [Cape's confidential optical character recognition service](https://docs.capeprivacy.com/cape-hosted/ocr) from the SDKs: [cape-js](https://docs.capeprivacy.com/sdks/javascript-sdk) and [pycape](https://pydocs.capeprivacy.com/). 4 | 5 | Before invoking the OCR service from one of the SDKs, you must [sign up](https://docs.capeprivacy.com/getting-started#sign-up-for-cape) with Cape and generate a [personal access token](https://docs.capeprivacy.com/reference/user-tokens). 6 | 7 | For this example, we will run the OCR on the PDF `claude_shannon.pdf`. 8 | 9 | ## From cape-js 10 | 11 | Before invoking the OCR, set the environment variable `CAPE_AUTH_TOKEN` to your [personal access token](https://docs.capeprivacy.com/reference/user-tokens). 12 | ``` 13 | export CAPE_AUTH_TOKEN="your cape auth token" 14 | ``` 15 | 16 | To run the OCR from cape-js on the PDF, run: 17 | ``` 18 | node run_ocr.mjs 19 | ``` 20 | 21 | To encrypt the PDF with [cape.encrypt](https://docs.capeprivacy.com/tutorials/encrypting#cape-encrypt), then invoke the OCR on the encrypted PDF, run: 22 | ``` 23 | node encrypt_run_ocr.mjs 24 | ``` 25 | 26 | ## From pycape 27 | 28 | Before invoking the OCR, set the environment variable `CAPE_AUTH_TOKEN` to your [personal access token](https://docs.capeprivacy.com/reference/user-tokens). 29 | 30 | ``` 31 | export CAPE_AUTH_TOKEN="your cape auth token" 32 | ``` 33 | 34 | To run the OCR from pycape on the PDF, run: 35 | ``` 36 | python run_ocr.py 37 | ``` 38 | 39 | To encrypt the PDF with [cape.encrypt](https://docs.capeprivacy.com/tutorials/encrypting#cape-encrypt), then invoke the OCR on the encrypted PDF, run: 40 | ``` 41 | python encrypt_run_ocr.py 42 | ``` -------------------------------------------------------------------------------- /ocr/encrypt_run_ocr.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | Cape 3 | } from "@capeprivacy/cape-sdk"; 4 | import * as fs from "fs"; 5 | import * as crypto from "crypto"; 6 | import * as pkijs from "pkijs"; 7 | 8 | 9 | // If you run this script from a node environment 10 | // set the engine to "nodeEngine" 11 | const name = "nodeEngine"; 12 | pkijs.setEngine( 13 | name, 14 | new pkijs.CryptoEngine({ 15 | name, 16 | crypto: crypto.webcrypto 17 | }) 18 | ); 19 | 20 | // Load your PDF 21 | const pdf = fs.readFileSync("./Claude_Shannon.pdf"); 22 | 23 | // Get a personal access token from the UI or the CLI with 24 | // cape token create --name ocr 25 | const authToken = process.env.CAPE_AUTH_TOKEN; 26 | 27 | // Instantiate a Cape object with your auth token and the URL 28 | // "wss://ocr.capeprivacy.com". Setting the URL to wss://ocr.capeprivacy.com" 29 | // will guarantee the OCR model is deployed to larger instances with required 30 | // dependencies. 31 | const cape = new Cape({ 32 | authToken, 33 | enclaveUrl: "wss://ocr.capeprivacy.com", 34 | }); 35 | 36 | try { 37 | // Encrypt your PDF with cape.encrypt. When invoking this method, by default, 38 | // the SDK will retrieve the public encryption key associated with 39 | // your account 40 | const encryptedPdf = await cape.encrypt(pdf); 41 | 42 | // Invoke the OCR service on your encrypted PDF by setting the function ID to 43 | // "capedocs/ocr-doctr-onnx-1.0" 44 | const result = await cape.run({ 45 | id: "capedocs/ocr-doctr-onnx-1.0", 46 | data: encryptedPdf, 47 | }); 48 | 49 | // Print OCR transcript 50 | console.log(JSON.parse(result).ocr_transcript); 51 | 52 | } catch (error) { 53 | console.error("Something went wrong", error); 54 | } -------------------------------------------------------------------------------- /ocr/encrypt_run_ocr.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from pycape import Cape 5 | 6 | 7 | auth_token_env = os.environ.get("CAPE_AUTH_TOKEN") 8 | user_name_env = os.environ.get("USERNAME") 9 | 10 | # Load your PDF 11 | with open("Claude_Shannon.pdf", "rb") as f: 12 | pdf = f.read() 13 | 14 | # Instantiate a Cape object with the URL "wss://ocr.capeprivacy.com". 15 | # Setting the URL to "wss://ocr.capeprivacy.com" will guarantee the OCR model is 16 | # deployed to larger instances with required dependencies. 17 | cape = Cape(url="wss://ocr.capeprivacy.com") 18 | 19 | # Get a personal access token from the UI or the CLI with 20 | # cape token create --name ocr 21 | t = cape.token(auth_token_env) 22 | 23 | # Encrypt your PDF with cape.encrypt. When invoking this method, by default, 24 | # the SDK will retrieve the public encryption key associated with 25 | # your account 26 | encrypted_pdf = cape.encrypt(pdf) 27 | 28 | # Select the Cape function you would like to invoke. 29 | # Since we want invoke the ocr service, set the function ID 30 | # to "capedocs/ocr-doctr-onnx-1.0" 31 | f = cape.function("capedocs/ocr-doctr-onnx-1.0") 32 | 33 | # Invoke the OCR service 34 | result = cape.run(f, t, encrypted_pdf) 35 | 36 | # Print the transcript 37 | print(f"OCR transcript: {json.loads(result)['ocr_transcript']}") 38 | 39 | # Print the bounding boxes 40 | print(f"OCR records: {json.loads(result)['ocr_records']}") 41 | -------------------------------------------------------------------------------- /ocr/run_ocr.mjs: -------------------------------------------------------------------------------- 1 | import { 2 | Cape 3 | } from "@capeprivacy/cape-sdk"; 4 | import * as fs from "fs"; 5 | import * as crypto from "crypto"; 6 | import * as pkijs from "pkijs"; 7 | 8 | 9 | // If you run this script from a node environment 10 | // set the engine to "nodeEngine" 11 | const name = "nodeEngine"; 12 | pkijs.setEngine( 13 | name, 14 | new pkijs.CryptoEngine({ 15 | name, 16 | crypto: crypto.webcrypto 17 | }) 18 | ); 19 | 20 | // Load your PDF 21 | const pdf = fs.readFileSync("./Claude_Shannon.pdf"); 22 | 23 | // Get a personal access token from the UI or the CLI with 24 | // cape token create --name ocr 25 | const authToken = process.env.CAPE_AUTH_TOKEN; 26 | 27 | // Instantiate a Cape object with your auth token and the URL 28 | // "wss://ocr.capeprivacy.com". Setting the URL to "wss://ocr.capeprivacy.com" 29 | // will guarantee the OCR model is deployed to larger instances with required 30 | // dependencies. 31 | const cape = new Cape({ 32 | authToken, 33 | enclaveUrl: "wss://ocr.capeprivacy.com", 34 | }); 35 | 36 | try { 37 | // Invoke the OCR service on your encrypted PDF by setting the function ID to 38 | // "capedocs/ocr-doctr-onnx-1.0" 39 | const result = await cape.run({ 40 | id: "capedocs/ocr-doctr-onnx-1.0", 41 | data: pdf, 42 | }); 43 | 44 | // Print OCR transcript 45 | console.log(JSON.parse(result).ocr_transcript); 46 | 47 | } catch (error) { 48 | console.error("Something went wrong", error); 49 | } -------------------------------------------------------------------------------- /ocr/run_ocr.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | 4 | from pycape import Cape 5 | 6 | 7 | auth_token_env = os.environ.get("CAPE_AUTH_TOKEN") 8 | user_name_env = os.environ.get("USERNAME") 9 | 10 | # Load your PDF 11 | with open("Claude_Shannon.pdf", "rb") as f: 12 | pdf = f.read() 13 | 14 | # Instantiate a Cape object with the URL "wss://ocr.capeprivacy.com". 15 | # Setting the URL to "wss://ocr.capeprivacy.com.com" will guarantee the OCR model is 16 | # deployed to larger instances with required dependencies. 17 | cape = Cape(url="wss://ocr.capeprivacy.com") 18 | 19 | # Get a personal access token from the UI or the CLI with 20 | # cape token create --name ocr 21 | t = cape.token(auth_token_env) 22 | 23 | # Select the Cape function you would like to invoke. 24 | # Since we want invoke the ocr service, set the function ID 25 | # to "capedocs/ocr-doctr-onnx-1.0" 26 | f = cape.function("capedocs/ocr-doctr-onnx-1.0") 27 | 28 | # Invoke the OCR service 29 | result = cape.run(f, t, pdf) 30 | 31 | # Print the transcript 32 | print(f"OCR transcript: {json.loads(result)['ocr_transcript']}") 33 | 34 | # Print the bounding boxes 35 | print(f"OCR records: {json.loads(result)['ocr_records']}") 36 | -------------------------------------------------------------------------------- /pendulum/README.md: -------------------------------------------------------------------------------- 1 | # Pendulum 2 | 3 | In this example, we show how to run a function requiring a dependency. The function simply returns the current time based on a specific timezone using the [Pendulum](https://pendulum.eustace.io/) library. The dependency is listed in `requirements.txt`. 4 | 5 | **Build the deployment package:** 6 | ```console 7 | # Create a deployment folder 8 | $ mkdir pendulum-deployment 9 | # Copy the cape function (app.py) in the deployment folder 10 | $ cp app.py pendulum-deployment/. 11 | # Add pendulum as dependencies using docker 12 | $ docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target pendulum-deployment/ 13 | ``` 14 | **Deploy the function:** 15 | 16 | Deploy with the CLI the function as follow: 17 | ```console 18 | $ cape deploy pendulum-deployment/ 19 | Deploying function to Cape ... 20 | Success! Deployed function to Cape. 21 | Function ID ➜ 22 | Function Checksum ➜ 23 | Function Name ➜ 24 | ``` 25 | 26 | **Run the function:** 27 | You can run the function with the cli as follow. Just make sure to use the function id returned by `cape deploy`. 28 | ```console 29 | 30 | cape run Europe/Paris 31 | ``` -------------------------------------------------------------------------------- /pendulum/app.py: -------------------------------------------------------------------------------- 1 | import pendulum 2 | 3 | 4 | def cape_handler(timezone_bytes): 5 | timezone = timezone_bytes.decode() 6 | now = pendulum.now(timezone) 7 | return now.to_time_string() 8 | -------------------------------------------------------------------------------- /pendulum/requirements.txt: -------------------------------------------------------------------------------- 1 | pendulum==2.1.2 -------------------------------------------------------------------------------- /reader/deploy/app.py: -------------------------------------------------------------------------------- 1 | from io import BufferedReader 2 | from io import StringIO 3 | import logging 4 | import mimetypes 5 | import os 6 | from typing import Optional, Tuple 7 | import uuid 8 | 9 | import docx2txt 10 | import fitz 11 | import pandas 12 | import pptx 13 | import json 14 | import base64 15 | 16 | logger = logging.getLogger("reader") 17 | 18 | excel_mimetypes = [ 19 | "application/vnd.ms-excel", 20 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", 21 | "application/vnd.ms-excel.sheet.macroEnabled.12", 22 | "application/vnd.ms-excel.sheet.binary.macroEnabled.12", 23 | ] 24 | 25 | mimetypes_to_human = { 26 | "application/vnd.ms-excel": "spreadsheet", 27 | "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": "spreadsheet", 28 | "application/vnd.ms-excel.sheet.macroEnabled.12": "spreadsheet", 29 | "application/vnd.ms-excel.sheet.binary.macroEnabled.12": "spreadsheet", 30 | "application/octet-stream": "bytes", 31 | "text/markdown": "markdown", 32 | "text/plain": "plaintext", 33 | "text/csv": "csv", 34 | "application/pdf": "pdf", 35 | "application/vnd.openxmlformats-officedocument.wordprocessingml.document": "docx", 36 | "application/vnd.openxmlformats-officedocument.presentationml.presentation": "powerpoint", 37 | } 38 | 39 | 40 | class UnsupportedFileError(Exception): 41 | pass 42 | 43 | 44 | def extract_text_from_filepath( 45 | filepath: str, mimetype: Optional[str] = None 46 | ) -> Tuple[str, str]: 47 | """Return the text content of a file given its filepath.""" 48 | 49 | if not mimetype or mimetype == "application/octet-stream": 50 | # Get the mimetype of the file based on its extension 51 | mimetype, _ = mimetypes.guess_type(filepath) 52 | logger.info(f"guessed mimetype {mimetype}") 53 | 54 | if not mimetype: 55 | if filepath.endswith(".md"): 56 | mimetype = "text/markdown" 57 | else: 58 | raise UnsupportedFileError( 59 | "Unsupported file type, could not determine mimetype" 60 | ) 61 | 62 | try: 63 | with open(filepath, "rb") as file: 64 | # extracting text can be slow so run in threadpool 65 | extracted_text = extract_text_from_file(file, mimetype) 66 | except Exception as e: 67 | logger.error(f"Error extracting text from file: {e}") 68 | raise e 69 | 70 | return extracted_text, mimetype 71 | 72 | 73 | def extract_text_from_file(file: BufferedReader, mimetype: str) -> str: 74 | if mimetype == "application/pdf": 75 | # Extract text from pdf using PyMuPDF 76 | with fitz.open(file) as pdf: 77 | extracted_text = " ".join([page.get_text() for page in pdf]) 78 | 79 | elif mimetype == "text/plain" or mimetype == "text/markdown": 80 | # Read text from plain text file 81 | try: 82 | # if there are any errors with decoding then replace those bytes with `?`. 83 | # this handles the situation where a file may contain invalid bytes but is otherwise 84 | # correct 85 | extracted_text = file.read().decode("utf-8", errors="replace") 86 | except UnicodeDecodeError as e: 87 | logger.error(f"uploaded file was not utf-8 encoded {e}") 88 | raise UnsupportedFileError("uploaded file was not utf-8 encoded") 89 | elif ( 90 | mimetype 91 | == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" 92 | ): 93 | # Extract text from docx using docx2txt 94 | extracted_text = docx2txt.process(file) 95 | elif mimetype == "text/csv": 96 | # Extract text from csv using csv module 97 | extracted_text = "" 98 | 99 | # if there are any errors with decoding then replace those bytes with `?`. 100 | # this handles the situation where a file may contain invalid bytes but is otherwise 101 | # correct 102 | for line in file: 103 | extracted_text += line.decode("utf-8", errors="replace") 104 | elif ( 105 | mimetype 106 | == "application/vnd.openxmlformats-officedocument.presentationml.presentation" 107 | ): 108 | # Extract text from pptx using python-pptx 109 | extracted_text = "" 110 | presentation = pptx.Presentation(file) 111 | for slide in presentation.slides: 112 | for shape in slide.shapes: 113 | if shape.has_text_frame: 114 | for paragraph in shape.text_frame.paragraphs: 115 | for run in paragraph.runs: 116 | extracted_text += run.text + " " 117 | extracted_text += "\n" 118 | elif mimetype in excel_mimetypes: 119 | extracted_text = "" 120 | df = pandas.read_excel(file, sheet_name=None) 121 | for j in df: 122 | b = StringIO() 123 | df[j].to_csv(b) 124 | extracted_text += b.getvalue() + "\n\n" 125 | else: 126 | # Unsupported file type 127 | raise UnsupportedFileError(f"Unsupported file type: {mimetype}") 128 | 129 | return extracted_text 130 | 131 | 132 | # Extract text from a file based on its mimetype 133 | def extract_text_from_form_file(data, mimetype, filename): 134 | _, ext = os.path.splitext(filename) 135 | temp_file_path = f"temp_file-{uuid.uuid4()}{ext}" 136 | 137 | # write the file to a temporary location 138 | with open(temp_file_path, "wb") as f: 139 | f.write(data) 140 | 141 | try: 142 | extracted_text, mimetype = extract_text_from_filepath( 143 | temp_file_path, mimetype 144 | ) 145 | except Exception as e: 146 | logger.error(f"Error extracting text from filepath: {e}") 147 | os.remove(temp_file_path) 148 | 149 | raise e 150 | 151 | # remove file from temp location 152 | os.remove(temp_file_path) 153 | 154 | return extracted_text 155 | 156 | 157 | def cape_handler(data): 158 | inp = json.loads(data) 159 | d = base64.b64decode(inp["data"]) 160 | return extract_text_from_form_file(d, inp["mimetype"], inp["filename"]) 161 | -------------------------------------------------------------------------------- /reader/requirements.txt: -------------------------------------------------------------------------------- 1 | PyMuPDF==1.22.5 2 | python-pptx==0.6.21 3 | docx2txt==0.8 4 | pandas==2.0.0 5 | -------------------------------------------------------------------------------- /secure_search/README.md: -------------------------------------------------------------------------------- 1 | # Secure Search 2 | 3 | Simulates a cybersecurity search function, where the IP addresses you are interested in need to remain private but the data/logs you are searching might be public. The function defines selectors (IP addresses of interest) as variables: 4 | ```python 5 | selectors_a = "172.19.0.4" 6 | selectors_b = "1.2.3.4" 7 | ``` 8 | and accepts a list of json objects containing IP addresses in a "hosts" element: 9 | ```json 10 | [{"protocol": "tcp", "hosts": "1.2.3.4", "state": "ESTABLISHED"}, {...}] 11 | ``` 12 | It will return True or False for each selector that you are interested in, for each object in the json list: 13 | ```json 14 | [{"172.19.0.4": false, "1.2.3.4": true}, {...}] 15 | ``` 16 | It uses a user-supplied public key to encrypt the results within the enclave prior to returning them to the caller. Please see: 17 | * `./test/generate_keys.py`: for how to generate a keypair. 18 | * `./test/test_cape.py`: a sample pycape client which calls the function, receives an encrypted response, and performs decryption using the private key. 19 | 20 | ``` 21 | cape deploy ../secure_search 22 | ``` -------------------------------------------------------------------------------- /secure_search/app.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hybrid_pke 3 | 4 | # "selectors" are the strings to search for (you can customize these) 5 | selectors_a = "172.19.0.4" 6 | selectors_b = "1.2.3.4" 7 | 8 | # Expects a string representation of a list of json objects like: 9 | # [{"protocol": "tcp", "hosts": "172.18.0.4:29092", "state": "ESTABLISHED"}, {...}] 10 | # Each json object MUST have a "hosts" element. 11 | # Uses a bundled public key to decrypt results (named public_keyset.json). 12 | def cape_handler(input): 13 | as_str = input.decode('utf-8') 14 | req = json.loads(as_str) 15 | 16 | def encrypt(input): 17 | hpke = hybrid_pke.default() 18 | with open('public_keyset.bin', 'rb') as key_bytes: 19 | key = key_bytes.read() 20 | info = b"" 21 | aad = b"" 22 | encap, ciphertext = hpke.seal(key, info, aad, bytes(json.dumps(input), 'utf-8')) 23 | 24 | return encap + ciphertext 25 | 26 | def find(input): 27 | a = False 28 | b = False 29 | if selectors_a in input["hosts"]: 30 | a = True 31 | if selectors_b in input["hosts"]: 32 | b = True 33 | 34 | return { selectors_a: a, selectors_b: b } 35 | 36 | finds = list(map(find, req)) 37 | 38 | # return results in plaintext 39 | # return finds 40 | 41 | # alternatively, use this to encrypt the results with the supplied public key 42 | return encrypt(finds) -------------------------------------------------------------------------------- /secure_search/hybrid_pke-1.0.0.dist-info/INSTALLER: -------------------------------------------------------------------------------- 1 | pip 2 | -------------------------------------------------------------------------------- /secure_search/hybrid_pke-1.0.0.dist-info/METADATA: -------------------------------------------------------------------------------- 1 | Metadata-Version: 2.1 2 | Name: hybrid_pke 3 | Version: 1.0.0 4 | Classifier: License :: OSI Approved :: Apache Software License 5 | Classifier: Programming Language :: Rust 6 | Classifier: Programming Language :: Python 7 | Classifier: Programming Language :: Python :: Implementation :: CPython 8 | Classifier: Topic :: Security :: Cryptography 9 | License-File: LICENSE 10 | License-File: NOTICE 11 | Summary: The Hybrid Public Key Encryption (HPKE) standard in Python 12 | Requires-Python: >=3.8 13 | Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM 14 | Project-URL: repository, https://github.com/capeprivacy/hybrid-pke 15 | 16 | Hybrid PKE 17 | =============== 18 | The Hybrid Public Key Encryption (HPKE) standard in Python. 19 | 20 | `hybrid_pke` = [`hpke-rs`](https://github.com/franziskuskiefer/hpke-rs) :heavy_plus_sign: [`PyO3`](https://github.com/PyO3/pyo3) 21 | 22 | This library provides Python bindings to the `hpke-rs` crate, which supports primitives from either [Rust Crypto](https://github.com/RustCrypto) or [EverCrypt](https://hacl-star.github.io/HaclValeEverCrypt.html). 23 | 24 |
25 | Table of Contents 26 |
    27 |
  1. Usage
  2. 28 |
  3. Features
  4. 29 |
  5. Installation
  6. 30 |
  7. Development
  8. 31 |
  9. Related Projects
  10. 32 |
33 |
34 | 35 | ## Usage 36 | ### Basic 37 | The single-shot API is intended for single message encryption/decryption. The default HPKE configuration uses the unauthenticated Base mode, an X25519 DH key encapsulation mechanism, a SHA256 key derivation mechanism, and a ChaCha20Poly1305 AEAD function. 38 | 39 | ```python 40 | import hybrid_pke 41 | 42 | hpke = hybrid_pke.default() 43 | info = b"" # shared metadata, correspondance-level 44 | aad = b"" # shared metadata, message-level 45 | secret_key_r, public_key_r = hpke.generate_key_pair() # receiver keys, pre-generated 46 | 47 | # ============== Sender ============== 48 | 49 | message = b"hello from the other side!" 50 | encap, ciphertext = hpke.seal(public_key_r, info, aad, message) 51 | 52 | # ============= Receiver ============= 53 | 54 | plaintext = hpke.open(encap, secret_key_r, info, aad, ciphertext) 55 | print(plaintext.decode("utf-8")) 56 | # >> hello from the other side! 57 | ``` 58 | 59 | ### Advanced 60 | 61 |
Sender & Receiver Contexts 62 | 63 | The Sender Context and Receiver Context APIs allow for setting up a context for repeated encryptions and decryptions. It's recommended whenever you intend to perform several encryptions or decryptions in quick succession. 64 | ```python 65 | info = b"quotes from your favorite aphorists" 66 | aads = [ 67 | b"Szasz", 68 | b"Nietzsche", 69 | b"Morandotti", 70 | b"Brudzinski", 71 | b"Hubbard", 72 | ] 73 | 74 | # ============== Sender ============== 75 | 76 | messages = [ 77 | b"Two wrongs don't make a right, but they make a good excuse.", 78 | b"Become who you are!", 79 | b"Only those who aren't hungry are able to judge the quality of a meal.", 80 | b"Under certain circumstances a wanted poster is a letter of recommendation.", 81 | b"Nobody ever forgets where he buried the hatchet.", 82 | ] 83 | encap, sender_context = hpke.setup_sender(public_key_r, info) 84 | 85 | ciphertexts = [] 86 | for aad, msg in zip(aads, messages): 87 | ciphertext = sender_context.seal(aad, msg) 88 | ciphertexts.append(ciphertext) 89 | 90 | # ============= Receiver ============= 91 | 92 | receiver_context = hpke.setup_receiver(encap, secret_key_r, info) 93 | plaintexts = [] 94 | for aad, ctxt in zip(aads, ciphertexts): 95 | plaintext = receiver_context.open(aad, ctxt) 96 | plaintexts.append(plaintext) 97 | 98 | print(f"\"{plaintexts[0].decode()}\" - {aad[0].decode()}") 99 | print(f"\"{plaintexts[1].decode()}\" - {aad[1].decode()}") 100 | # >> "Two wrongs don't make a right, but they make a good excuse." - Szasz 101 | # >> "Become who you are!" - Nietzsche 102 | ``` 103 |
104 | 105 |
Mode.AUTH: Authenticated Sender 106 | 107 | Auth mode allows for signing and verifying encryptions with a previously authenticated sender key-pair. 108 | ```python 109 | hpke = hybrid_pke.default(mode=hybrid_pke.Mode.AUTH) 110 | secret_key_r, public_key_r = hpke.generate_key_pair() # receiver keys 111 | secret_key_s, public_key_s = hpke.generate_key_pair() # sender keys, pre-authenticated 112 | 113 | # ============== Sender ============== 114 | 115 | # sign with sender's secret key 116 | encap, ciphertext = hpke.seal(public_key_r, info, aad, message, sk_s=secret_key_s) 117 | 118 | # ============= Receiver ============= 119 | 120 | # verify with sender's public key 121 | plaintext = hpke.open(encap, secret_key_r, info, aad, ciphertext, pk_s=public_key_s) 122 | ``` 123 |
124 | 125 |
Mode.PSK: Pre-shared Key Authentication 126 | 127 | PSK mode allows for signing and verifying encryptions with a previously shared key held by both the sender and recipient. 128 | ```python 129 | hpke = hybrid_pke.default(mode=hybrid_pke.Mode.PSK) 130 | # pre-shared key + ID 131 | psk = bytes.fromhex("0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82") 132 | psk_id = bytes.fromhex("456e6e796e20447572696e206172616e204d6f726961") 133 | 134 | # ============== Sender ============== 135 | 136 | # sign with pre-shared key 137 | encap, ciphertext = hpke.seal(public_key_r, info, aad, message, psk=psk, psk_id=psk_id) 138 | 139 | # ============= Receiver ============= 140 | 141 | # verify with pre-shared key 142 | plaintext = hpke.open(encap, secret_key_r, info, aad, ciphertext, psk=psk, psk_id=psk_id) 143 | ``` 144 |
145 | 146 |
Mode.AUTH_PSK: Combining AUTH and PSK. 147 | 148 | PSK mode allows for signing and verifying encryptions with a previously shared key held by both the sender and recipient. 149 | ```python 150 | hpke = hybrid_pke.default(mode=hybrid_pke.Mode.PSK) 151 | secret_key_r, public_key_r = hpke.generate_key_pair() # receiver keys 152 | secret_key_s, public_key_s = hpke.generate_key_pair() # sender keys, pre-authenticated 153 | # pre-shared key + ID 154 | psk = bytes.fromhex("0247fd33b913760fa1fa51e1892d9f307fbe65eb171e8132c2af18555a738b82") 155 | psk_id = bytes.fromhex("456e6e796e20447572696e206172616e204d6f726961") 156 | 157 | # ============== Sender ============== 158 | 159 | # sign with both pre-shared key and sender's secret key 160 | encap, ciphertext = hpke.seal( 161 | public_key_r, info, aad, message, 162 | psk=psk, psk_id=psk_id, sk_s=secret_key_s, 163 | ) 164 | 165 | # ============= Receiver ============= 166 | 167 | # verify with both pre-shared key and sender's public key 168 | plaintext = hpke.open( 169 | encap, secret_key_r, info, aad, ciphertext, 170 | psk=psk, psk_id=psk_id, pk_s=public_key_s, 171 | ) 172 | ``` 173 |
174 | 175 |

(back to top)

176 | 177 | ## Features 178 | The features available match those supported by `hpke-rs`. 179 | 180 |
HPKE Modes 181 | 182 | - [x] mode_base 183 | - [x] mode_psk 184 | - [x] mode_auth 185 | - [x] mode_auth_psk 186 |
187 | 188 |
KEMs: (Diffie-Hellman) Key Encapsulation Mechanisms 189 | 190 | - [x] DHKEM(P-256, HKDF-SHA256) 191 | - [ ] DHKEM(P-384, HKDF-SHA384) 192 | - [ ] DHKEM(P-521, HKDF-SHA512) 193 | - [x] DHKEM(X25519, HKDF-SHA256) 194 | - [ ] DHKEM(X448, HKDF-SHA512) 195 |
196 | 197 |
KDFs: Key Derivation Functions 198 | 199 | - [x] HKDF-SHA256 200 | - [x] HKDF-SHA384 201 | - [x] HKDF-SHA512 202 |
203 | 204 |
AEADs: Authenticated Encryption with Additional Data functions 205 | 206 | - [x] AES-128-GCM 207 | - [x] AES-256-GCM 208 | - [x] ChaCha20Poly1305 209 | - [x] Export only 210 |
211 | 212 |

(back to top)

213 | 214 | ## Installation 215 | Wheels for various platforms and architectures can be found on [PyPI](https://pypi.org/project/hybrid-pke/) or in the `wheelhouse.zip` archive from the [latest Github release](https://github.com/capeprivacy/hybrid-pke/releases). 216 | 217 | The library can also be installed from source with [`maturin`](https://github.com/PyO3/maturin) -- see below. 218 | 219 |

(back to top)

220 | 221 | ## Development 222 | 223 | We use [`maturin`](https://github.com/PyO3/maturin) to build and distribute the PyO3 extension module as a Python wheel. 224 | 225 | For users of `cmake`, we provide a [`Makefile`](https://github.com/capeprivacy/hybrid-pke/blob/main/Makefile) that includes some helpful development commands. 226 | 227 |
Some useful tips 228 | 229 | - `maturin develop` builds & installs the Python package into your Python environment (`venv` or `conda` recommended) 230 | - `pytest .` tests the resulting Python package. 231 | - `pytest -n auto .` runs the full test suite in parallel. 232 | - `maturin build --release -o dist --sdist` builds the extension module in release-mode and produces a wheel for your environment's OS and architecture. 233 | - The `-i`/`--interpreter` flag for `maturin` can be used to swap out different Python interpreters, if you have multiple Python installations. 234 |
235 | 236 |

(back to top)

237 | 238 | ## Related Projects 239 | - [hpke-py](https://github.com/ctz/hpke-py): An implementation of HPKE based on primitives from [cryptography.io](https://cryptography.io). 240 | 241 |

(back to top)

242 | -------------------------------------------------------------------------------- /secure_search/hybrid_pke-1.0.0.dist-info/RECORD: -------------------------------------------------------------------------------- 1 | hybrid_pke-1.0.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4 2 | hybrid_pke-1.0.0.dist-info/METADATA,sha256=hku7PSpOxaOz-hj5a6lv-L9cYM71VuDgKebwjNXdxm0,8644 3 | hybrid_pke-1.0.0.dist-info/RECORD,, 4 | hybrid_pke-1.0.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 5 | hybrid_pke-1.0.0.dist-info/WHEEL,sha256=ZlLpGR_cCmuV6ST0pIH_gSIyOAQq6NIXGJBXpco8pb4,107 6 | hybrid_pke-1.0.0.dist-info/license_files/LICENSE,sha256=dp-Atby0LtCvTk0v104ayb-EPLgMWikhnR7zVEQoprs,10846 7 | hybrid_pke-1.0.0.dist-info/license_files/NOTICE,sha256=c-n4hThwl8QFoN5U3IublPQNMBzhmzm7S3N0M1HD16Y,278 8 | hybrid_pke/__init__.py,sha256=6cW890LmUjnyxoaD8ol1w7_vsh9-39ucrNhGByoJvxs,123 9 | hybrid_pke/__pycache__/__init__.cpython-39.pyc,, 10 | hybrid_pke/hybrid_pke.cpython-39-x86_64-linux-gnu.so,sha256=O1w7Ud0aKzCxoxERZA8VCGiRz9cZOqOvNBb1CWfSTUg,4540280 11 | -------------------------------------------------------------------------------- /secure_search/hybrid_pke-1.0.0.dist-info/REQUESTED: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/secure_search/hybrid_pke-1.0.0.dist-info/REQUESTED -------------------------------------------------------------------------------- /secure_search/hybrid_pke-1.0.0.dist-info/WHEEL: -------------------------------------------------------------------------------- 1 | Wheel-Version: 1.0 2 | Generator: maturin (0.13.1) 3 | Root-Is-Purelib: false 4 | Tag: cp39-cp39-manylinux_2_28_x86_64 5 | -------------------------------------------------------------------------------- /secure_search/hybrid_pke-1.0.0.dist-info/license_files/LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. -------------------------------------------------------------------------------- /secure_search/hybrid_pke-1.0.0.dist-info/license_files/NOTICE: -------------------------------------------------------------------------------- 1 | Hybrid-PKE 2 | 3 | All contributions by Cape Privacy: 4 | Copyright (c) 2022, Cape, Inc. 5 | All rights reserved. 6 | 7 | All other contributions: 8 | Copyright (c) 2022, the respective contributors. 9 | All rights reserved. 10 | 11 | This project includes software developed by Cape, Inc (https://capeprivacy.com/). 12 | -------------------------------------------------------------------------------- /secure_search/hybrid_pke/__init__.py: -------------------------------------------------------------------------------- 1 | from .hybrid_pke import * 2 | 3 | __doc__ = hybrid_pke.__doc__ 4 | if hasattr(hybrid_pke, "__all__"): 5 | __all__ = hybrid_pke.__all__ -------------------------------------------------------------------------------- /secure_search/hybrid_pke/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/secure_search/hybrid_pke/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /secure_search/hybrid_pke/__pycache__/__init__.cpython-39.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/secure_search/hybrid_pke/__pycache__/__init__.cpython-39.pyc -------------------------------------------------------------------------------- /secure_search/hybrid_pke/hybrid_pke.cpython-39-x86_64-linux-gnu.so: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/secure_search/hybrid_pke/hybrid_pke.cpython-39-x86_64-linux-gnu.so -------------------------------------------------------------------------------- /secure_search/input.search.data: -------------------------------------------------------------------------------- 1 | [{ 2 | "protocol": "tcp", 3 | "hosts": "1.2.3.4:29092", 4 | "state": "ESTABLISHED" 5 | }, 6 | { 7 | "protocol": "tcp", 8 | "hosts": "5.6.7.8:29092", 9 | "state": "ESTABLISHED" 10 | }, 11 | { 12 | "protocol": "tcp", 13 | "hosts": "1.2.3.4:29092<>172.19.0.4:29092", 14 | "state": "ESTABLISHED" 15 | }] -------------------------------------------------------------------------------- /secure_search/public_keyset.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/secure_search/public_keyset.bin -------------------------------------------------------------------------------- /secure_search/requirements.txt: -------------------------------------------------------------------------------- 1 | hybrid_pke==1.0.0 2 | -------------------------------------------------------------------------------- /secure_search/test/generate_keys.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hybrid_pke 3 | 4 | 5 | def generate_keypair(): 6 | hpke = hybrid_pke.default() 7 | secret_key_r, public_key_r = hpke.generate_key_pair() # receiver keys, pre-generated 8 | 9 | # include this with your function, and use it to encrypt responses 10 | with open('private_keyset.bin', 'wb') as f: 11 | f.write(secret_key_r) 12 | 13 | # keep this safe; it's used to decrypt 14 | with open('public_keyset.bin', 'wb') as f: 15 | f.write(public_key_r) 16 | 17 | generate_keypair() -------------------------------------------------------------------------------- /secure_search/test/private_keyset.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/secure_search/test/private_keyset.bin -------------------------------------------------------------------------------- /secure_search/test/public_keyset.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/secure_search/test/public_keyset.bin -------------------------------------------------------------------------------- /secure_search/test/test_cape.py: -------------------------------------------------------------------------------- 1 | from pycape import Cape 2 | from pycape import FunctionRef 3 | import json 4 | import hybrid_pke 5 | 6 | def decrypt(ciphertext): 7 | # 32 bytes (KEM-derived public key) + 45 bytes (ciphertext of ptxt) = 77 bytes 8 | encap = ciphertext[0:32] 9 | ciphertext = ciphertext[32:] 10 | hpke = hybrid_pke.default() 11 | with open('private_keyset.bin', 'rb') as key_bytes: 12 | key = key_bytes.read() 13 | info = b"" 14 | aad = b"" 15 | plaintext = hpke.open(encap, key, info, aad, ciphertext) 16 | 17 | return plaintext 18 | 19 | # Cape search function info 20 | function_id = "ZRtuUk7zcijjVy5F7uE9r7" 21 | function_checksum = "ec8dce345048468ad2697e79d2a44086b6cf499f322c3e00bd91ee5b5097ede9" 22 | f = FunctionRef(function_id, function_checksum) 23 | 24 | # connect to Cape function 25 | client = Cape() 26 | client.connect(function_id) 27 | 28 | # TEST 1: false for 172.19.0.4; true for 1.2.3.4 29 | resp_ciphertext = client.invoke(b'[{"protocol": "tcp", "hosts": "1.2.3.4", "state": "ESTABLISHED"}]') 30 | 31 | # TEST 2: false for both IPs 32 | #resp_ciphertext = client.invoke(b'[{"protocol": "tcp", "hosts": "0.0.0.0", "state": "ESTABLISHED"}]') 33 | 34 | print(f'\nResponse from Cape (ciphertext):') 35 | print(resp_ciphertext) 36 | 37 | plaintext = decrypt(resp_ciphertext) 38 | print(f'\nDecrypted response:') 39 | print(plaintext) 40 | 41 | client.close() -------------------------------------------------------------------------------- /secure_search/test/test_keys_locally.py: -------------------------------------------------------------------------------- 1 | import json 2 | import hybrid_pke 3 | 4 | 5 | def encrypt(input): 6 | hpke = hybrid_pke.default() 7 | with open('public_keyset.bin', 'rb') as key_bytes: 8 | key = key_bytes.read() 9 | info = b"" 10 | aad = b"" 11 | encap, ciphertext = hpke.seal(key, info, aad, input) 12 | 13 | return encap + ciphertext 14 | 15 | def decrypt(ciphertext): 16 | # 32 bytes (KEM-derived public key) + 45 bytes (ciphertext of ptxt) = 77 bytes 17 | encap = ciphertext[0:32] 18 | ciphertext = ciphertext[32:] 19 | hpke = hybrid_pke.default() 20 | with open('private_keyset.bin', 'rb') as key_bytes: 21 | key = key_bytes.read() 22 | info = b"" 23 | aad = b"" 24 | plaintext = hpke.open(encap, key, info, aad, ciphertext) 25 | 26 | return plaintext 27 | 28 | data = b'Hello World!' 29 | ciphertext = encrypt(data) 30 | print(f'\nCiphertext:') 31 | print(ciphertext) 32 | 33 | plaintext = decrypt(ciphertext) 34 | print(f'\nDecrypted data:') 35 | print(plaintext) -------------------------------------------------------------------------------- /sentiment_analysis/README.md: -------------------------------------------------------------------------------- 1 | # Sentiment Analysis 2 | 3 | This application performs secure sentiment analysis using a tflite model 4 | 5 | ## Usage 6 | 7 | ### Prepare deployment folder with dependencies 8 | ``` 9 | docker run -v `pwd`:/build -w /build --rm -it python:3.9-slim-bullseye pip install -r requirements.txt --target ./deploy/ 10 | ``` 11 | 12 | ### Deploy and run with Cape 13 | 14 | ``` 15 | cape deploy ./deploy 16 | 17 | Deploying function to Cape ... 18 | Success! Deployed function to Cape 19 | Function ID ➜ 8QoFCwrCu4sVd2wDAXxnbV 20 | Function Hash ➜ f420da150d095b9a6151d34dec7b9483bd1e618d5d6ca0b20a6813189c10c45b 21 | ``` 22 | 23 | ``` 24 | cape run 8QoFCwrCu4sVd2wDAXxnbV -f input.pos.data 25 | 26 | ('The sentiment is: ', 'positive', ' with a probability of ', 78.08290123939514, '%.') 27 | ``` 28 | 29 | ``` 30 | cape run 8QoFCwrCu4sVd2wDAXxnbV -f input.neg.data 31 | 32 | ('The sentiment is: ', 'negative', ' with a probability of ', 86.57390475273132, '%.') 33 | ``` -------------------------------------------------------------------------------- /sentiment_analysis/deploy/app.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | from tflite_runtime.interpreter import Interpreter 3 | import contractions 4 | 5 | def load_vocab(path): 6 | vocabulary = {} 7 | with open(path, "r") as f: 8 | for i, line in enumerate(f.readlines()): 9 | item = line.strip().split(" ") 10 | word = item[0] 11 | encoding = int(item[1]) 12 | vocabulary[word] = encoding 13 | return vocabulary 14 | 15 | def vectorize_text(text, vocabulary, input_shape): 16 | encoded_text = [] 17 | 18 | # Fix contractions 19 | expanded_words = [] 20 | for word in text.split(): 21 | expanded_words.append(contractions.fix(word)) 22 | text = " ".join(expanded_words) 23 | 24 | text = text.split(" ") 25 | for word in text: 26 | word = word.lower() # convert to lower case 27 | # account for words not in vocabulary 28 | if word in vocabulary.keys(): 29 | word_encoding = vocabulary[word] 30 | else: 31 | word_encoding = vocabulary[""] 32 | encoded_text.append(word_encoding) 33 | encoded_text = np.array(encoded_text, dtype=np.int32) 34 | encoded_text = np.pad( 35 | encoded_text, (0, input_shape[1] - len(encoded_text)), "constant" 36 | ) 37 | encoded_text = np.reshape(encoded_text, (input_shape[0], input_shape[1])) 38 | return encoded_text 39 | 40 | def cape_handler(text): 41 | text = text.decode("utf-8") 42 | 43 | # Load vocabulary 44 | vocabulary = load_vocab("./vocab.txt") 45 | 46 | # Load the TFLite model and allocate tensors. 47 | interpreter = Interpreter(model_path="./model.tflite") 48 | interpreter.allocate_tensors() 49 | 50 | # Get input and output tensors. 51 | input_details = interpreter.get_input_details() 52 | output_details = interpreter.get_output_details() 53 | 54 | # Predict 55 | input_shape = input_details[0]["shape"] 56 | input_data = vectorize_text( 57 | text=text, vocabulary=vocabulary, input_shape=input_shape 58 | ) 59 | interpreter.set_tensor(input_details[0]["index"], input_data) 60 | interpreter.invoke() 61 | 62 | output_data = interpreter.get_tensor(output_details[0]["index"]) 63 | output_result = np.argmax(output_data) 64 | 65 | if output_result == 1: 66 | result = "positive" 67 | else: 68 | result = "negative" 69 | 70 | prob = output_data[0][output_result] * 100 71 | return (str(float(f'{prob:.2f}')) + "% " + result) or "You've stumped me! Please try a different phrase." -------------------------------------------------------------------------------- /sentiment_analysis/deploy/labels.txt: -------------------------------------------------------------------------------- 1 | negative 2 | positive -------------------------------------------------------------------------------- /sentiment_analysis/deploy/model.tflite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/capeprivacy/functions/282388877f79d34329a445de4d5cef7e928e982e/sentiment_analysis/deploy/model.tflite -------------------------------------------------------------------------------- /sentiment_analysis/input.neg.data: -------------------------------------------------------------------------------- 1 | this was a terrible film -------------------------------------------------------------------------------- /sentiment_analysis/input.pos.data: -------------------------------------------------------------------------------- 1 | this was a great film -------------------------------------------------------------------------------- /sentiment_analysis/requirements.txt: -------------------------------------------------------------------------------- 1 | numpy==1.23.2 2 | tflite-runtime==2.10.0 3 | contractions==0.1.72 --------------------------------------------------------------------------------