diff --git a/README.md b/README.md index b109da4..37cb408 100644 --- a/README.md +++ b/README.md @@ -2,18 +2,24 @@ ## Explanation -This example code shows how to use the advanced Device Request APIs available from the Ionic Platform. +This example code shows how to use the advanced [Device Request APIs](https://dev.ionic.com/api/device) available from the Machina Platform. It is meant to serve as sample code for developers learning about those APIs to use as reference. -Most developers will instead prefer to use Ionic's supported SDKs, which include a Python SDK which has the same - functionality shown in these examples, as well as significant additional features. +Most developers will instead prefer to use [Ionic's supported SDK](https://dev.ionic.com/sdk/features), which include a Python SDK which has the same + functionality shown in these examples, as well as significant additional features. There are SDK examples for [Create Key](https://dev.ionic.com/sdk/tasks/create-key?language=python) and [Get Key](https://dev.ionic.com/sdk/tasks/get-key?language=python). ## Setting up Environment +You will need to obtain a tenant. A free tenant can be obtained [here](https://ionic.com/start-for-free/). By following the prompted path, your +device will be enrolled. + +## Setting up Development Environment + You may want to use Python's virtualenv toolkit to manage your environment. Once loaded, install the pre-requisites: -```bash + +``` pip install -r requirements.txt ``` @@ -21,19 +27,21 @@ pip install -r requirements.txt ### Create and Fetch Keys: -The `example.py` tool shows how to create keys, and then request them again. +The `example.py` sample shows how to create keys, and then request them again. These two operations are usually done independently. -Using this example requires a Secure Enrollment Profile (SEP), which it expects via the plaintext profile persistor in a file `profiles.pt`. +Using this example requires a Secure Enrollment Profile (SEP), which it expects via the plaintext profile persistor in a file `$HOME/.ionicsecurity/profiles.pt`. Read [Enrollment Overview](https://dev.ionic.com/registration.html) to learn more. -See the Enrollment Example for obtaining one if you don't have one via another mechanism. +See **Enrolling** below if you didn't enroll via another mechanism. + +This example shows how to use the [Create Key API](https://dev.ionic.com/api/device/create-key) and the [Get Key API](https://dev.ionic.com/api/device/get-key). -### Enrolling: +### Enrolling The `example_enroll.py` tool shows enrolling a device and obtaining a SEP, and then storing it using the plaintext profile persistor. Using this example requires first editing the code to define the correct values for the variables. -After setting those values, it can be run and will produce `profiles.pt` which is the SEP stored in plaintext. +After setting those values, it can be run and will produce `$HOME/.ionicsecurity/profiles.pt` which is the SEP stored in plaintext. There are two options for setting the values: diff --git a/__init__.py b/__init__.py index 0321118..34b9cfc 100644 --- a/__init__.py +++ b/__init__.py @@ -5,7 +5,7 @@ # using builtin and 3rd-party libraries instead of the # # Ionic SDK. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # diff --git a/example.py b/example.py index 42b50d5..ed33c9b 100644 --- a/example.py +++ b/example.py @@ -5,17 +5,17 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = rmspeers, QA = # ########################################################### +import os from keys import create_keys, fetch_keys from persistors import ProfilePersistorPlaintext @@ -29,18 +29,23 @@ if __name__ == "__main__": - persistor = ProfilePersistorPlaintext('profiles.pt') + persistor_path = os.path.expanduser("~/.ionicsecurity/profiles.pt") + persistor = ProfilePersistorPlaintext(persistor_path) ionic_sep = persistor.get_active_profile() + print("") + print("Current Device ID: " + getattr(ionic_sep, "deviceId")) + print("") # Best practice is to include key attributes to describe the type of data you will be using this key to protect: ## These can either be `ionic-protected-*` prefixed so Ionic.com can't see them, and only other requestors who ## access the key can; or they can be unencrypted so that Ionic.com can use their values in policy decisions. dictKeyAttrs = { - 'classification': 'Public', + 'classification': ['Public'], 'ionic-protected-test': ['encrypted_value_1'] } created_keys = create_keys(ionic_sep, dictKeyAttrs) print('Created keys: {}'.format(created_keys)) + print("") # Now we show fetching one of these keys back: # NOTE: We may or may not be able to get it depending on the current data policy. diff --git a/example_enroll.py b/example_enroll.py index f6dd951..cc6cb88 100644 --- a/example_enroll.py +++ b/example_enroll.py @@ -5,18 +5,18 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = rmspeers, QA = jmassey # ########################################################### import sys +import os from registration import create_device from registration import get_ionic_token @@ -42,8 +42,10 @@ stoken = "" uidauth = "" -api_url = "https://dev-api.ionic.com/" -enrollment_server_url = "https://dev-enrollment.ionic.com/" +# These URLs are valid if you obtained your tenant using Start for Free, https://ionic.com/start-for-free/. +# Modify the keyspace to the keyspace of your tenant. +api_url = "https://api.ionic.com/" +enrollment_server_url = "https://enrollment.ionic.com/" keyspace = "ABcd" @@ -68,6 +70,7 @@ # NOTE: This will overwrite any existing content at that path. persistor = ProfilePersistorPlaintext() persistor.add_sep(sep, set_as_active=True) - persistor.set_file_path("profiles.pt") + persistor_path = os.path.expanduser("~/.ionicsecurity/profiles.pt") + persistor.set_file_path(persister_path) print(persistor) persistor.save_to_json() diff --git a/example_external_ids.py b/example_external_ids.py index 366c9b3..9a458ef 100644 --- a/example_external_ids.py +++ b/example_external_ids.py @@ -5,15 +5,14 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = rmspeers, QA = # ########################################################### from uuid import uuid4 diff --git a/keys/__init__.py b/keys/__init__.py index 3b1f781..8fa449c 100644 --- a/keys/__init__.py +++ b/keys/__init__.py @@ -5,7 +5,7 @@ # using builtin and 3rd-party libraries instead of the # # Ionic SDK. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # diff --git a/keys/key_create.py b/keys/key_create.py index e3bf1ce..d36355d 100644 --- a/keys/key_create.py +++ b/keys/key_create.py @@ -5,15 +5,14 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = daniel/rmspeers, QA = jmassey # ########################################################### import base64 @@ -303,12 +302,15 @@ def create_key_transaction(ionic_sep, dictKeyAttrs, dictMetadata, send_full_hfp= ### Handling the Key Create Response ### ######################################## # Assume the response from Ionic is a successful 200, and we have created keys with the provided attributes. - assert (key_create_response.status_code == 200) or (key_create_response.status_code == 401) + status_code = key_create_response.status_code + assert (status_code == 200) or (status_code == 201), "\nKey Create response status code: %d\n" % status_code return key_create_response, cid, b64encoded_signed_attributes_iv_cipher_text_aad_as_string def create_keys(ionic_sep, dictKeyAttrs = {}, dictMetadata = {}): + # See https://dev.ionic.com/api/device/create-key for more information on key create. + key_create_response, cid, b64encoded_signed_attributes_iv_cipher_text_aad_as_string = create_key_transaction(ionic_sep, dictKeyAttrs, dictMetadata) decrypted_envelope, response_cid = utilities.decrypt_envelope(ionic_sep, key_create_response, cid) diff --git a/keys/key_fetch.py b/keys/key_fetch.py index 5aacd56..04972b1 100644 --- a/keys/key_fetch.py +++ b/keys/key_fetch.py @@ -5,15 +5,14 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = daniel, QA = jmassey # ########################################################### import base64 @@ -131,57 +130,19 @@ def fetch_key_request(ionic_sep, protection_keys, external_id=None, send_full_hf headers={'Content-Type': 'application/json'}) # Assume the response from Ionic is a successful 200 and that we have received keys for the provided key tags. - assert (key_fetch_response.status_code == 200) or (key_fetch_response.status_code == 401) + status_code = key_fetch_response.status_code + assert (status_code == 200) or (status_code == 201), "\nKey Fetch response status code: %d\n" % status_code return key_fetch_response, cid -def decrypt_envelope(ionic_sep, key_fetch_response, cid): - ####################################### - ### Handling the Key Fetch Response ### - ####################################### - - key_fetch_response_body = key_fetch_response.json() - - # As a precaution, ensure that the client's CID is the same as the response's CID. - response_cid = key_fetch_response_body['cid'] - assert cid == response_cid - - # Base 64 decode the envelope's value. - decoded_key_fetch_response_envelope_as_bytes = base64.b64decode(key_fetch_response_body['envelope']) - - # Prepare to decrypt the `envelope` contents. - - # Obtain the initialization vector which is the first 16 bytes. - initialization_vector_from_response_envelope = decoded_key_fetch_response_envelope_as_bytes[:16] - - # Obtain the data to decrypt which is the bytes between the initializaiton vector and the tag. - cipher_text_from_response_envelope = decoded_key_fetch_response_envelope_as_bytes[16:-16] - - # Obtain the tag which is the last 16 bytes. - gcm_tag_from_response_envelope = decoded_key_fetch_response_envelope_as_bytes[-16:] - - # Construct a cipher to decrypt the data. - cipher = Cipher(algorithms.AES(ionic_sep.aesCdIdcKey), - modes.GCM(initialization_vector_from_response_envelope, - gcm_tag_from_response_envelope), - backend=default_backend() - ).decryptor() - - # Set the cipher's `aad` as the value of the `cid`. - cipher.authenticate_additional_data(response_cid.encode(encoding='utf-8')) - - # Decrypt the ciphertext. - decrypted_key_response_bytes = cipher.update(cipher_text_from_response_envelope) + cipher.finalize() - decrypted_envelope = json.loads(decrypted_key_response_bytes.decode(encoding='utf-8')) - - return decrypted_envelope - - def fetch_keys(ionic_sep, protection_keys, external_ids=None): ########################################## ### Constructing the Key Fetch Request ### ########################################## + + # See https://dev.ionic.com/api/device/get-key for more information on key fetch. + example_key_fetch_body = """ { "cid": "CID|MfyG..A.ec095b70-c1d0-4ac0-9d0f-2cafa82b8a1f|1487622171374|1487622171374|5bFnTQ==", @@ -216,7 +177,7 @@ def fetch_keys(ionic_sep, protection_keys, external_ids=None): "the full HFP included.") key_fetch_response, cid = fetch_key_request(ionic_sep, protection_keys, send_full_hfp=True) - decrypted_envelope = decrypt_envelope(ionic_sep, key_fetch_response, cid) + decrypted_envelope, _ = utilities.decrypt_envelope(ionic_sep, key_fetch_response, cid) # Pull out any query results as well to return: query_results = decrypted_envelope['data'].get('query-results') diff --git a/keys/utilities.py b/keys/utilities.py index 9a7acf6..7ca93d0 100644 --- a/keys/utilities.py +++ b/keys/utilities.py @@ -5,10 +5,10 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # @@ -41,15 +41,23 @@ def make_cid(device_id): def decrypt_envelope(ionic_sep, server_response, cid): - response_body = server_response.json() + ####################################### + ### Handling the Key Fetch Response ### + ####################################### + + # See https://dev.ionic.com/api/device/get-key for more information on key fetch. + + key_fetch_response_body = server_response.json() # As a precaution, ensure that the client's CID is the same as the response's CID. - response_cid = response_body['cid'] + response_cid = key_fetch_response_body['cid'] if cid != response_cid: raise ValueError("The CID in the response did not match the one from the request.") # Base 64 decode the envelope's value. - decoded_response_envelope_as_bytes = base64.b64decode(response_body['envelope']) + decoded_response_envelope_as_bytes = base64.b64decode(key_fetch_response_body['envelope']) + # Prepare to decrypt the `envelope` contents. + # Prepare to decrypt the `envelope` contents. # Obtain the initialization vector which is the first 16 bytes. @@ -64,15 +72,15 @@ def decrypt_envelope(ionic_sep, server_response, cid): # Construct a cipher to decrypt the data. cipher = Cipher(algorithms.AES(ionic_sep.aesCdIdcKey), modes.GCM(initialization_vector_from_response_envelope, - gcm_tag_from_response_envelope), + gcm_tag_from_response_envelope), backend=default_backend() ).decryptor() # Set the cipher's `aad` as the value of the `cid` cipher.authenticate_additional_data(response_cid.encode(encoding='utf-8')) - decrypted_key_response_bytes = cipher.update(cipher_text_from_response_envelope) + cipher.finalize() # Decrypt the ciphertext. + decrypted_key_response_bytes = cipher.update(cipher_text_from_response_envelope) + cipher.finalize() decrypted_envelope = json.loads(decrypted_key_response_bytes.decode(encoding='utf-8')) return decrypted_envelope, response_cid diff --git a/persistors/__init__.py b/persistors/__init__.py index d72ef29..6ba0e51 100644 --- a/persistors/__init__.py +++ b/persistors/__init__.py @@ -5,7 +5,7 @@ # using builtin and 3rd-party libraries instead of the # # Ionic SDK. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # diff --git a/persistors/profile.py b/persistors/profile.py index 71d7838..ca1579b 100644 --- a/persistors/profile.py +++ b/persistors/profile.py @@ -5,15 +5,14 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = rmspeers, QA = # ########################################################### import binascii diff --git a/persistors/profile_persistor_plaintext.py b/persistors/profile_persistor_plaintext.py index e1da22f..faf1d44 100644 --- a/persistors/profile_persistor_plaintext.py +++ b/persistors/profile_persistor_plaintext.py @@ -5,15 +5,14 @@ # using built-in and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = rmspeers, QA = jmassey # ########################################################### import json diff --git a/registration/__init__.py b/registration/__init__.py index ce62c4d..21aa018 100644 --- a/registration/__init__.py +++ b/registration/__init__.py @@ -5,7 +5,7 @@ # using builtin and 3rd-party libraries instead of the # # Ionic SDK. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # diff --git a/registration/get_ionic_token.py b/registration/get_ionic_token.py index 68899fe..8fcc00f 100644 --- a/registration/get_ionic_token.py +++ b/registration/get_ionic_token.py @@ -7,15 +7,14 @@ # using builtin and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = jmassey, QA = rmspeers # ############################################################ import requests @@ -44,6 +43,9 @@ def get_authn(enrollment_url): def get_assertion(user, password, idp_url, saml_body): + # See https://dev.ionic.com/platform/enrollment/saml for more information on + # SAML authentication. + data = {"user": user, "password": password, "SAMLRequest": saml_body} login_response = requests.post(idp_url, data) saml_assertion = login_response.headers.get("X-Saml-Response", None) diff --git a/registration/register.py b/registration/register.py index 31a7ae9..f100bf2 100644 --- a/registration/register.py +++ b/registration/register.py @@ -5,15 +5,14 @@ # using builtin and 3rd-party libraries instead of the # # Ionic SDK. # # # -# This example uses Python 3.4.3 # +# This example uses Python 3.4.3 or higher. # # This example is best read with syntax highlighting on. # # # -# (c) 2017 Ionic Security Inc. # +# (c) 2017-2020 Ionic Security Inc. # # Confidential and Proprietary # # By using this code, I agree to the Terms & Conditions # # (https://www.ionic.com/terms-of-use/) and the Privacy # # Policy (https://www.ionic.com/privacy-notice/) # -# Author = jmassey/rmspeers, QA = daniel # ############################################################ import binascii diff --git a/requirements.txt b/requirements.txt index b8c533c..c44d4bd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,11 +1,11 @@ -appdirs==1.4.0 -cffi==1.9.1 -cryptography==1.7.2 -idna==2.2 -packaging==16.8 -pyasn1==0.2.2 -pycparser==2.17 -pyOpenSSL==16.2.0 -pyparsing==2.1.10 -requests==2.13.0 -six==1.10.0 \ No newline at end of file +appdirs==1.4.4 +cffi==1.14.0 +cryptography==2.9.2 +idna==2.10 +packaging==20.4 +pyasn1==0.4.8 +pycparser==2.20 +pyOpenSSL==19.1.0 +pyparsing==2.4.7 +requests==2.24.0 +six==1.15.0