Skip to content

Commit

Permalink
Implement mock interface for testing without actual device
Browse files Browse the repository at this point in the history
  • Loading branch information
elonen committed Jul 25, 2024
1 parent e63779b commit dd14ef2
Show file tree
Hide file tree
Showing 18 changed files with 1,237 additions and 395 deletions.
28 changes: 28 additions & 0 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
name: Python Tests

on:
push:
branches: [ main ]
pull_request:
branches: [ main ]

jobs:
test:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2

- name: Set up Python
uses: actions/setup-python@v2
with:
python-version: '3.12'

- name: Install system dependencies
run: |
sudo apt-get update
sudo apt-get install -y make openssh-client openssl libpcsclite-dev
- name: Run tests
run: |
make test
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ build/

.DS_Store

*.pickle

_venv
.vscode
__pycache__
Expand Down
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: setup install clean distclean package
.PHONY: setup install clean distclean package test

# Configurable paths and settings
VENV := _venv
Expand Down Expand Up @@ -37,6 +37,9 @@ $(VENV): requirements.txt
$(PIP) install build
@touch $(VENV)

test: $(TARGET_BINS)
./run-tests.sh

clean:
@echo "Cleaning up build and Python file artifacts..."
@rm -rf $(VENV)
Expand Down
38 changes: 19 additions & 19 deletions hsm_secrets/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def get_domain_bitfield(self, names: set['HSMDomainName']) -> int:
assert 0 <= res <= 0xFFFF, f"Domain bitfield out of range: {res}"
return res

def find_def(self, id_or_label: Union[int, str], enforce_type: Optional[type] = None) -> 'HSMDefBase':
def find_def(self, id_or_label: Union[int, str], enforce_type: Optional[type] = None) -> 'HSMObjBase':
return _find_def_by_id_or_label(self, id_or_label, enforce_type)

@staticmethod
Expand Down Expand Up @@ -102,8 +102,8 @@ def algorithm_from_name(algo: Union['AsymmetricAlgorithm', 'SymmetricAlgorithm',


# Some type definitions for the models
KeyID = Annotated[int, Field(strict=True, gt=0, lt=0xFFFF)]
KeyLabel = Annotated[str, Field(max_length=40)]
HSMKeyID = Annotated[int, Field(strict=True, gt=0, lt=0xFFFF)]
HSMKeyLabel = Annotated[str, Field(max_length=40)]
HSMDomainNum = Annotated[int, Field(strict=True, gt=0, lt=17)]
HSMDomainName = Literal["all", "x509", "tls", "nac", "gpg", "codesign", "ssh", "password_derivation", "encryption"]

Expand All @@ -127,10 +127,10 @@ class General(NoExtraBaseModel):
x509_defaults: 'X509Info'


class HSMDefBase(NoExtraBaseModel):
class HSMObjBase(NoExtraBaseModel):
model_config = ConfigDict(extra="forbid")
label: KeyLabel
id: KeyID
label: HSMKeyLabel
id: HSMKeyID
domains: set[HSMDomainName]


Expand All @@ -140,14 +140,14 @@ class HSMDefBase(NoExtraBaseModel):
"none", "sign-pkcs", "sign-pss", "sign-ecdsa", "sign-eddsa", "decrypt-pkcs", "decrypt-oaep", "derive-ecdh",
"exportable-under-wrap", "sign-ssh-certificate", "sign-attestation-certificate"
]
class HSMAsymmetricKey(HSMDefBase):
class HSMAsymmetricKey(HSMObjBase):
capabilities: set[AsymmetricCapabilityName]
algorithm: AsymmetricAlgorithm

# -- Symmetric key models --
SymmetricAlgorithm = Literal["aes128", "aes192", "aes256"]
SymmetricCapabilityName = Literal["none", "encrypt-ecb", "decrypt-ecb", "encrypt-cbc", "decrypt-cbc", "exportable-under-wrap"]
class HSMSymmetricKey(HSMDefBase):
class HSMSymmetricKey(HSMObjBase):
capabilities: set[SymmetricCapabilityName]
algorithm: SymmetricAlgorithm

Expand All @@ -163,15 +163,15 @@ class HSMSymmetricKey(HSMDefBase):
"put-opaque", "put-otp-aead-key", "put-template", "put-wrap-key", "randomize-otp-aead", "reset-device",
"rewrap-from-otp-aead-key", "rewrap-to-otp-aead-key", "set-option", "sign-attestation-certificate", "sign-ecdsa",
"sign-eddsa", "sign-hmac", "sign-pkcs", "sign-pss", "sign-ssh-certificate", "unwrap-data", "verify-hmac", "wrap-data"]
class HSMWrapKey(HSMDefBase):
class HSMWrapKey(HSMObjBase):
capabilities: set[WrapCapabilityName]
delegated_capabilities: set[WrapDelegateCapabilityName]
algorithm: WrapAlgorithm

# -- HMAC key models --
HmacAlgorithm = Literal["hmac-sha1", "hmac-sha256", "hmac-sha384", "hmac-sha512"]
HmacCapabilityName = Literal["none", "sign-hmac", "verify-hmac", "exportable-under-wrap"]
class HSMHmacKey(HSMDefBase):
class HSMHmacKey(HSMObjBase):
capabilities: set[HmacCapabilityName]
algorithm: HmacAlgorithm

Expand All @@ -198,15 +198,15 @@ class HSMHmacKey(HSMDefBase):
"sign-eddsa", "sign-hmac", "sign-pkcs", "sign-pss", "sign-ssh-certificate", "unwrap-data", "verify-hmac", "wrap-data",
"decrypt-ecb", "encrypt-ecb", "decrypt-cbc", "encrypt-cbc",
]
class HSMAuthKey(HSMDefBase):
class HSMAuthKey(HSMObjBase):
capabilities: set[AuthKeyCapabilityName]
delegated_capabilities: set[AuthKeyDelegatedCapabilityName]

# -- Opaque object models --
OpaqueObjectAlgorithm = Literal["opaque-data", "opaque-x509-certificate"]
class HSMOpaqueObject(HSMDefBase):
class HSMOpaqueObject(HSMObjBase):
algorithm: OpaqueObjectAlgorithm
sign_by: Optional[KeyID] # ID of the key to sign the object with (if applicable)
sign_by: Optional[HSMKeyID] # ID of the key to sign the object with (if applicable)

# -- Helper models --
X509KeyUsage = Literal[
Expand Down Expand Up @@ -263,7 +263,7 @@ class X509(NoExtraBaseModel):
root_certs: List[X509Cert]

class TLS(NoExtraBaseModel):
default_ca_id: KeyID
default_ca_id: HSMKeyID
intermediate_certs: List[X509Cert]

class NAC(NoExtraBaseModel):
Expand All @@ -280,7 +280,7 @@ class SSHTemplateSlots(NoExtraBaseModel):
max: int

class SSH(NoExtraBaseModel):
default_ca: KeyID
default_ca: HSMKeyID
root_ca_keys: List[HSMAsymmetricKey]


Expand All @@ -290,16 +290,16 @@ class PwRotationToken(NoExtraBaseModel):
ts: Annotated[int, Field(strict=True, ge=0)]

class PasswordDerivationRule(NoExtraBaseModel):
id: KeyLabel
key: KeyID
id: HSMKeyLabel
key: HSMKeyID
format: Literal["bip39", "hex"] = Field(default="bip39")
separator: str = Field(default=".")
bits: Literal[64, 128, 256] = Field(default=64)
rotation_tokens: List[PwRotationToken] = Field(default_factory=list)

class PasswordDerivation(NoExtraBaseModel):
keys: List[HSMHmacKey]
default_rule: KeyLabel
default_rule: HSMKeyLabel
rules: List[PasswordDerivationRule]


Expand Down Expand Up @@ -362,7 +362,7 @@ def parse_keyid(key_id: str) -> int:
return int(key_id.replace('0x',''), 16)


def _find_def_by_id_or_label(conf: HSMConfig, id_or_label: Union[int, str], enforce_type: Optional[type] = None) -> HSMDefBase:
def _find_def_by_id_or_label(conf: HSMConfig, id_or_label: int|str, enforce_type: type|None = None) -> HSMObjBase:
"""
Find the configuration object for a given key ID or label.
:raises KeyError: If the key is not found in the configuration file.
Expand Down
Loading

0 comments on commit dd14ef2

Please sign in to comment.