diff --git a/aleksis/apps/kort/models/sdm.py b/aleksis/apps/kort/models/sdm.py index 55aed0f10433d7cd404bdb2bfed8be0faca131df..43b3517091f716a74cd7d0a6eb7e1c25d91a214f 100644 --- a/aleksis/apps/kort/models/sdm.py +++ b/aleksis/apps/kort/models/sdm.py @@ -1,11 +1,10 @@ from binascii import unhexlify +from typing import Optional from django.core.exceptions import BadRequest from django.http import HttpRequest -from libsdm import EncMode, InvalidMessage, decrypt_sun_message -from libsdm.derive import derive_tag_key, derive_undiversified_key -from libsdm.util import parse_parameters +from pylibsdm.backend.validate import ParamValidator from aleksis.core.mixins import ExtensibleModel, ObjectAuthenticator from aleksis.core.util.core_helpers import get_site_preferences @@ -17,48 +16,36 @@ class NfcSdmAuthenticator(ObjectAuthenticator): """Object authenticator using NFC SDM.""" name = "nfc_sdm" - require_lrp = False - def authenticate(self, request: HttpRequest, obj: ExtensibleModel): + def authenticate(self, request: HttpRequest, obj: Optional[ExtensibleModel]): """SUN decrypting authenticator""" - master_key = unhexlify(get_site_preferences()["kort__sdm_master_key"]) - try: - param_mode, picc_enc_data, enc_file_data, sdmmac = parse_parameters(request.GET) - except ValueError as e: - raise BadRequest(**e.args) + file_read_key = unhexlify(get_site_preferences()["kort__sdm_file_read_key"]) + meta_read_key = unhexlify(get_site_preferences()["kort__sdm_meta_read_key"]) - try: - res = decrypt_sun_message( - param_mode=param_mode, - sdm_meta_read_key=derive_undiversified_key(master_key, 1), - sdm_file_read_key=lambda uid: derive_tag_key(master_key, uid, 2), - picc_enc_data=picc_enc_data, - sdmmac=sdmmac, - enc_file_data=enc_file_data, - ) - except InvalidMessage: - raise BadRequest("Invalid SUN message or signature") - if self.require_lrp and res["encryption_mode"] != EncMode.LRP: - raise BadRequest("LRP required") + validator = ParamValidator(file_read_key, meta_read_key) + validator.parse_uri(request.build_absolute_uri()) - # FIXME do tag tamper check here + if validator.uid is None or validator.read_ctr is None or not validator.cmac_valid: + raise BadRequest("Invalid SUN message or signature") try: - card = Card.objects.get(chip_number__iexact=res["uid"].hex()) + card = Card.objects.get(chip_number__iexact=validator.uid.hex()) except Card.DoesNotExist: return False + if obj is None: + if self.object is None: + self.object = card.person + obj = card.person + else: + obj = self.object + if card.person != obj: raise BadRequest("Card is not linked to identified object") - if card.last_read_counter >= res["read_ctr"]: + if card.last_read_counter >= validator.read_ctr: raise BadRequest("Read counter went backwards, possible replay attack") - card.last_read_counter = res["read_ctr"] + card.last_read_counter = validator.read_ctr card.save() return True - - -class NfcSdmAuthenticatorRequireLrp(NfcSdmAuthenticator): - name = "nfc_sdm_lrp" - require_lrp = True diff --git a/aleksis/apps/kort/preferences.py b/aleksis/apps/kort/preferences.py index 633d513e58736e8ad6c034185d46b664a76670c3..adcfc8b0f4309e9293914cee0a878ca27482fb6f 100644 --- a/aleksis/apps/kort/preferences.py +++ b/aleksis/apps/kort/preferences.py @@ -11,11 +11,22 @@ kort = Section("kort", verbose_name=_("Student ID Cards")) @site_preferences_registry.register -class SdmMasterKey(StringPreference): - """SDM master key for NFC.""" +class SdmFileReadKey(StringPreference): + """SDM file read key for NFC.""" section = kort - name = "sdm_master_key" + name = "sdm_file_read_key" default = hexlify(bytes(16)) - verbose_name = _("SDM Master Key") + verbose_name = _("SDM file read key") + required = False + + +@site_preferences_registry.register +class SdmMetaReadKey(StringPreference): + """SDM meta read key for NFC.""" + + section = kort + name = "sdm_meta_read_key" + default = hexlify(bytes(16)) + verbose_name = _("SDM meta read key") required = False diff --git a/pyproject.toml b/pyproject.toml index 293147e210c21b4f48db557fc1e74e943b006db5..a4f542ce6c0ee474ccb77cbf56b6b4c2d1a4a307 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,21 +1,22 @@ [tool.poetry] name = "AlekSIS-App-Kort" version = "0.2" -packages = [ - { include = "aleksis" } -] +packages = [{ include = "aleksis" }] readme = "README.rst" include = ["CHANGELOG.rst", "LICENCE.rst", "aleksis/**/*.mo"] description = "AlekSIS (School Information System) — App Kort (manage student IDs)" -authors = ["Margarete Grassl <grasslma@katharineum.de>", "Jonathan Weth <dev@jonathanweth.de>"] +authors = [ + "Margarete Grassl <grasslma@katharineum.de>", + "Jonathan Weth <dev@jonathanweth.de>", +] license = "EUPL-1.2" homepage = "https://aleksis.org" repository = "https://edugit.org/AlekSIS/onboarding/AlekSIS-App-Kort" classifiers = [ - "Environment :: Web Environment", - "Intended Audience :: Education", - "Topic :: Education" + "Environment :: Web Environment", + "Intended Audience :: Education", + "Topic :: Education", ] [[tool.poetry.source]] @@ -29,6 +30,7 @@ aleksis-core = "^3.0" python-barcode = "^0.14.0" django-formtools = "^2.3" django-ace = "^1.0.12" +pylibsdm = "^1.0.0a0.dev2" [tool.poetry.dev-dependencies] aleksis-builddeps = "*"