Skip to content

Commit

Permalink
Merge pull request #2 from godaddy/mvp
Browse files Browse the repository at this point in the history
Initial MVP of Asherah library
  • Loading branch information
jgowdy-godaddy authored Mar 4, 2022
2 parents abff352 + ab719e2 commit 64b729f
Show file tree
Hide file tree
Showing 12 changed files with 1,181 additions and 1 deletion.
38 changes: 38 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# http://editorconfig.org

root = true

[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
charset = utf-8
tab_width = 4
max_line_length = 120

# Use 2 spaces for the HTML files
[*.html]
indent_size = 2

# The JSON files contain newlines inconsistently
[*.json]
indent_size = 2

# Minified JavaScript files shouldn't be changed
[**.min.js]

# Makefiles always use tabs for indentation
[Makefile]
indent_style = tab

# Batch files use tabs for indentation
[*.bat]
indent_style = tab

[*.md]
trim_trailing_whitespace = false

[*.{yaml,yml}]
indent_size = 2
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ __pycache__/

# C extensions
*.so
*.dylib
*.dll

# Distribution / packaging
.Python
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
## [0.1.0] - 2022-03-04

- Initial release
31 changes: 30 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,30 @@
# asherah-python
# asherah-python

Asherah envelope encryption and key rotation library

This is a wrapper of the Asherah Go implementation using the Cobhan FFI library

Example code:

```python
from asherah import Asherah, AsherahConfig

config = AsherahConfig(
kms_type='static',
metastore='memory',
service_name='TestService',
product_id='TestProduct',
verbose=True,
session_cache=True
)
crypt = Asherah()
crypt.setup(config)

data = b"mysecretdata"

encrypted = crypt.encrypt("partition", data)
print(encrypted)

decrypted = crypt.decrypt("partition", encrypted)
print(decrypted)
```
4 changes: 4 additions & 0 deletions asherah/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .asherah import Asherah
from .types import AsherahConfig

__all__ = ["Asherah", "AsherahConfig"]
142 changes: 142 additions & 0 deletions asherah/asherah.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import os
from cobhan import Cobhan
from datetime import datetime, timezone
from typing import ByteString, Union

from . import exceptions, types


class Asherah:
KEY_SIZE = 64

def __init__(self):
self.__cobhan = Cobhan()
self.__libasherah = self.__cobhan.load_library(
os.path.join(os.path.dirname(__file__), "libasherah"),
"libasherah",
"""
int32_t Setup(void* kmsTypePtr, void* metastorePtr, void* rdbmsConnectionStringPtr, void* dynamoDbEndpointPtr, void* dynamoDbRegionPtr, void* dynamoDbTableNamePtr, int32_t enableRegionSuffixInt, void* serviceNamePtr, void* productIdPtr, void* preferredRegionPtr, void* regionMapPtr, int32_t verboseInt, int32_t sessionCacheInt, int32_t debugOutputInt);
int32_t Decrypt(void* partitionIdPtr, void* encryptedDataPtr, void* encryptedKeyPtr, int64_t created, void* parentKeyIdPtr, int64_t parentKeyCreated, void* outputDecryptedDataPtr);
int32_t Encrypt(void* partitionIdPtr, void* dataPtr, void* outputEncryptedDataPtr, void* outputEncryptedKeyPtr, void* outputCreatedPtr, void* outputParentKeyIdPtr, void* outputParentKeyCreatedPtr);
""",
)

def setup(self, config: types.AsherahConfig) -> None:
kms_type_buf = self.__cobhan.str_to_buf(config.kms_type)
metastore_buf = self.__cobhan.str_to_buf(config.metastore)
service_name_buf = self.__cobhan.str_to_buf(config.service_name)
product_id_buf = self.__cobhan.str_to_buf(config.product_id)
rdbms_connection_string_buf = self.__cobhan.str_to_buf(
config.rdbms_connection_string
)
dynamo_db_endpoint_buf = self.__cobhan.str_to_buf(config.dynamo_db_endpoint)
dynamo_db_region_buf = self.__cobhan.str_to_buf(config.dynamo_db_region)
dynamo_db_table_name_buf = self.__cobhan.str_to_buf(config.dynamo_db_table_name)
enable_region_suffix_int = int(config.enable_region_suffix)
preferred_region_buf = self.__cobhan.str_to_buf(config.preferred_region)
region_map_buf = self.__cobhan.str_to_buf(config.region_map)
verbose_int = int(config.verbose)
session_cache_int = int(config.session_cache)
debug_output_int = int(config.debug_output)

result = self.__libasherah.Setup(
kms_type_buf,
metastore_buf,
rdbms_connection_string_buf,
dynamo_db_endpoint_buf,
dynamo_db_region_buf,
dynamo_db_table_name_buf,
enable_region_suffix_int,
service_name_buf,
product_id_buf,
preferred_region_buf,
region_map_buf,
verbose_int,
session_cache_int,
debug_output_int,
)
if result < 0:
raise exceptions.AsherahException(
f"Setup failed with error number {result}"
)

def encrypt(self, partition_id: str, data: Union[ByteString, str]):
if isinstance(data, str):
data = data.encode("utf-8")
# Inputs
partition_id_buf = self.__cobhan.str_to_buf(partition_id)
data_buf = self.__cobhan.bytearray_to_buf(data)
# Outputs
encrypted_data_buf = self.__cobhan.allocate_buf(len(data) + self.KEY_SIZE)
encrypted_key_buf = self.__cobhan.allocate_buf(self.KEY_SIZE)
created_buf = self.__cobhan.int_to_buf(0)
parent_key_id_buf = self.__cobhan.allocate_buf(self.KEY_SIZE)
parent_key_created_buf = self.__cobhan.int_to_buf(0)

result = self.__libasherah.Encrypt(
partition_id_buf,
data_buf,
encrypted_data_buf,
encrypted_key_buf,
created_buf,
parent_key_id_buf,
parent_key_created_buf,
)
if result < 0:
raise exceptions.AsherahException(
f"Encrypt failed with error number {result}"
)
data_row_record = types.DataRowRecord(
data=self.__cobhan.buf_to_bytearray(encrypted_data_buf),
key=types.EnvelopeKeyRecord(
encrypted_key=self.__cobhan.buf_to_bytearray(encrypted_key_buf),
created=datetime.fromtimestamp(
self.__cobhan.buf_to_int(created_buf), tz=timezone.utc
),
parent_key_meta=types.KeyMeta(
id=self.__cobhan.buf_to_str(parent_key_id_buf),
created=datetime.fromtimestamp(
self.__cobhan.buf_to_int(parent_key_created_buf),
tz=timezone.utc,
),
),
),
)

return data_row_record

def decrypt(
self, partition_id: str, data_row_record: types.DataRowRecord
) -> bytearray:
# Inputs
partition_id_buf = self.__cobhan.str_to_buf(partition_id)
encrypted_data_buf = self.__cobhan.bytearray_to_buf(data_row_record.data)
encrypted_key_buf = self.__cobhan.bytearray_to_buf(
data_row_record.key.encrypted_key
)
created = int(data_row_record.key.created.timestamp())
parent_key_id_buf = self.__cobhan.str_to_buf(
data_row_record.key.parent_key_meta.id
)
parent_key_created = int(
data_row_record.key.parent_key_meta.created.timestamp()
)
# Output
data_buf = self.__cobhan.allocate_buf(len(encrypted_data_buf) + self.KEY_SIZE)

result = self.__libasherah.Decrypt(
partition_id_buf,
encrypted_data_buf,
encrypted_key_buf,
created,
parent_key_id_buf,
parent_key_created,
data_buf,
)

if result < 0:
raise exceptions.AsherahException(
f"Decrypt failed with error number {result}"
)

return self.__cobhan.buf_to_bytearray(data_buf)
2 changes: 2 additions & 0 deletions asherah/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class AsherahException(Exception):
"""Base exception class for any problems encountered in Asherah"""
Empty file added asherah/py.typed
Empty file.
40 changes: 40 additions & 0 deletions asherah/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from dataclasses import dataclass
from datetime import datetime
from typing import ByteString


@dataclass
class AsherahConfig:
kms_type: str
metastore: str
service_name: str
product_id: str
rdbms_connection_string: str = None
dynamo_db_endpoint: str = None
dynamo_db_region: str = None
dynamo_db_table_name: str = None
enable_region_suffix: bool = False
preferred_region: str = None
region_map: str = None
verbose: bool = False
session_cache: bool = False
debug_output: bool = False


@dataclass
class KeyMeta:
id: str
created: datetime


@dataclass
class EnvelopeKeyRecord:
encrypted_key: ByteString
created: datetime
parent_key_meta: KeyMeta


@dataclass
class DataRowRecord:
data: ByteString
key: EnvelopeKeyRecord
10 changes: 10 additions & 0 deletions download-libasherah.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#!/bin/bash

rm -rf asherah/libasherah/

wget --content-disposition --directory-prefix asherah/libasherah/ \
https://github.com/godaddy/asherah-cobhan/releases/download/current/libasherah-arm64.dylib \
https://github.com/godaddy/asherah-cobhan/releases/download/current/libasherah-arm64.so \
https://github.com/godaddy/asherah-cobhan/releases/download/current/libasherah-x64.dylib \
https://github.com/godaddy/asherah-cobhan/releases/download/current/libasherah-x64.so \
|| exit 1
Loading

0 comments on commit 64b729f

Please sign in to comment.