Skip to content

Commit 9dfa523

Browse files
Merge pull request #48 from minvws/feat/hsm-label-response
updated pseudonym response to include additional data
2 parents c430838 + 9440d07 commit 9dfa523

15 files changed

Lines changed: 98 additions & 28 deletions

app.conf.example

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ hashing_key_id=abcdef1234567890
99
allow_insecure_requests = False
1010
# AES key-id used for encrypting and decrypting pseudonyms
1111
aes_key_id = 12345678
12+
# configurable AES mechanism to use, currently only AES_CBC is supported
13+
aes_mechanism = AES_CBC
1214

1315
[logging]
1416
# Syslog addresses in host:port format (UDP)

app/config.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@
66

77
from pydantic import BaseModel, ValidationError, Field
88

9+
from app.data import Pkc11Mechanism
10+
911
logger = logging.getLogger(__name__)
1012

1113
_PATH = "app.conf"
@@ -26,6 +28,7 @@ class ConfigApp(BaseModel):
2628
nvi_ura_number: str
2729
hashing_key_id: str
2830
aes_key_id: str
31+
aes_mechanism: Pkc11Mechanism
2932
allow_insecure_requests: bool = Field(default=False)
3033

3134

app/container.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@ def container_config(binder: inject.Binder) -> None:
1919
crypto_service: CryptoService
2020
if config.hsm_api.mock:
2121
logger.debug("Initializing mock crypto service")
22-
crypto_service = MockCryptoService()
22+
crypto_service = MockCryptoService(
23+
aes_key_id=config.app.aes_key_id, aes_mechanism=config.app.aes_mechanism
24+
)
2325
else:
2426
logger.debug(f"Initializing HSM API at {config.hsm_api.url}")
2527
http = HttpService(
@@ -36,6 +38,7 @@ def container_config(binder: inject.Binder) -> None:
3638
hash_key_id=config.app.hashing_key_id,
3739
support_sha1=config.hsm_api.support_sha1,
3840
aes_key_id=config.app.aes_key_id,
41+
aes_mechanism=config.app.aes_mechanism,
3942
)
4043

4144
binder.bind(CryptoService, crypto_service)

app/data.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from enum import StrEnum
2+
3+
4+
class Pkc11Mechanism(StrEnum):
5+
AES_CBC = "AES_CBC"
6+
SHA256_HMAC = "SHA256_HMAC"
7+
RSA_PKCS_OAEP = "RSA_PKCS_OAEP"

app/models/pseudonym.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,10 @@
44
class PseudonymRequest(BaseModel):
55
jwe: str
66
blind_factor: str
7+
8+
9+
class PseudonymResponse(BaseModel):
10+
encrypted_pseudonym: str
11+
iv: str
12+
label: str
13+
mechanism: str

app/routers/crypto.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,11 @@ def process(
4444
try:
4545
pseudonym = pseudonym_service.decrypt_and_unblind(data.jwe, data.blind_factor)
4646
hashed_pseudonym = pseudonym_service.hash(pseudonym)
47-
encrypted_pseudonym, iv = pseudonym_service.encrypt_pseudonym(
48-
pseudonym, hashed_pseudonym
47+
results = pseudonym_service.encrypt_pseudonym(
48+
pseudonym=pseudonym, hmac_hash=hashed_pseudonym
4949
)
5050
return JSONResponse(
51-
content={"encrypted_pseudonym": encrypted_pseudonym, "iv": iv},
51+
content=results.model_dump(),
5252
status_code=200,
5353
)
5454
except CryptoError as e:

app/services/crypto/crypto_service.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55

66
from jwcrypto import jwe
77

8+
from app.data import Pkc11Mechanism
89
from app.exceptions.exception import CryptoError, InvalidJweError
910

1011
logger = logging.getLogger(__name__)
@@ -15,6 +16,10 @@ class CryptoService(ABC):
1516
Interface for a cryptographic service that provides RSA key management, JWE decryption, and hashing.
1617
"""
1718

19+
def __init__(self, aes_key_id: str, aes_mechanism: Pkc11Mechanism) -> None:
20+
self.aes_key_id = aes_key_id
21+
self.aes_mechanism = aes_mechanism
22+
1823
@abstractmethod
1924
def health_check(self) -> bool:
2025
"""

app/services/crypto/hsm_api_crypto_service.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,19 @@
11
import base64
2-
from enum import StrEnum
32
import json
43
import logging
54

65
from Crypto.Cipher import AES
76
from requests import JSONDecodeError
87
from requests.exceptions import ConnectionError as RequestsConnectionError, Timeout
98

9+
from app.data import Pkc11Mechanism
1010
from app.exceptions.exception import CryptoError, InvalidJweError, KeyNotFoundError
1111
from app.services.crypto.crypto_service import CryptoService
1212
from app.services.http import HttpService
1313

1414
logger = logging.getLogger(__name__)
1515

1616

17-
class Pkc11Mechanism(StrEnum):
18-
AES_CBC = "AES_CBC"
19-
SHA256_HMAC = "SHA256_HMAC"
20-
RSA_PKCS_OAEP = "RSA_PKCS_OAEP"
21-
22-
2317
class HsmApiCryptoService(CryptoService):
2418
def __init__(
2519
self,
@@ -28,6 +22,7 @@ def __init__(
2822
slot: str,
2923
hash_key_id: str,
3024
aes_key_id: str,
25+
aes_mechanism: Pkc11Mechanism,
3126
support_sha1: bool = False,
3227
):
3328
logger.debug(f"Initializing HSM API service: module={module}, slot={slot}")
@@ -36,7 +31,7 @@ def __init__(
3631
self.slot = slot
3732
self.support_sha1 = support_sha1
3833
self.hash_key_id = hash_key_id
39-
self.aes_key_id = aes_key_id
34+
super().__init__(aes_key_id, aes_mechanism)
4035

4136
def health_check(self) -> bool:
4237
try:

app/services/crypto/mock_crypto_service.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import logging
33
from typing import Any
44

5+
from app.data import Pkc11Mechanism
56
from app.services.crypto.crypto_service import CryptoService
67

78
logger = logging.getLogger(__name__)
@@ -18,6 +19,9 @@ class MockCryptoService(CryptoService):
1819
Intended for local development and wiring smoke tests.
1920
"""
2021

22+
def __init__(self, aes_key_id: str, aes_mechanism: Pkc11Mechanism) -> None:
23+
super().__init__(aes_key_id, aes_mechanism)
24+
2125
def health_check(self) -> bool:
2226
return True
2327

app/services/pseudonym_service.py

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import base64
22
import logging
3-
from typing import Tuple
43

54
import pyoprf
65

76
from app.exceptions.exception import CryptoError, InvalidJweError
87
from app.logging.events import PSE_EXCHANGE_FAILED, PSE_EXCHANGE_OK, log_event
8+
from app.models.pseudonym import PseudonymResponse
99
from app.services.crypto.crypto_service import CryptoService
1010

1111
logger = logging.getLogger(__name__)
@@ -64,12 +64,22 @@ def decrypt_and_unblind(self, oprf_jwe: str, blind_factor: str) -> bytes:
6464
)
6565
return result
6666

67-
def encrypt_pseudonym(self, pseudonym: bytes, hmac_hash: bytes) -> Tuple[str, str]:
67+
def encrypt_pseudonym(
68+
self, pseudonym: bytes, hmac_hash: bytes
69+
) -> PseudonymResponse:
6870
iv = hmac_hash[:16]
6971
logger.debug("encrypting pseudonym")
7072
encrypted_data = self._crypto_service.encrypt_aes(pseudonym, iv)
7173
logger.debug("Pseudonym encrypted successfully")
72-
return encrypted_data, base64.urlsafe_b64encode(iv).decode()
74+
75+
label = self._crypto_service.aes_key_id
76+
mechanism = self._crypto_service.aes_mechanism
77+
return PseudonymResponse(
78+
encrypted_pseudonym=encrypted_data,
79+
iv=base64.urlsafe_b64encode(iv).decode(),
80+
label=label,
81+
mechanism=mechanism,
82+
)
7383

7484
def hash(self, pseudonym: bytes) -> bytes:
7585
logger.debug("Hashing pseudonym")

0 commit comments

Comments
 (0)