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 = "*"