123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112 |
- # Generated by Django 5.0.7 on 2024-07-27 18:49
- from django.db import migrations
- from django.conf import settings
- from fido2.utils import websafe_decode, websafe_encode
- from fido2.webauthn import AttestedCredentialData
- from fido2.cbor import encode
- import hashlib
- from allauth.mfa.adapter import get_adapter
- def migrate_mfa(apps, schema_editor):
- UserKey = apps.get_model("django_rest_mfa", "UserKey")
- Authenticator = apps.get_model("mfa", "Authenticator")
- adapter = get_adapter()
- authenticators = []
- for totp in UserKey.objects.filter(key_type="TOTP").iterator():
- recovery_codes = set()
- for backup_code in UserKey.objects.filter(
- key_type="Backup Codes", user_id=totp.user_id
- ):
- recovery_codes.update(backup_code.properties["codes"])
- secret = totp.properties["secret_key"]
- authenticators.append(
- Authenticator(
- user_id=totp.user_id,
- type="totp",
- data={"secret": adapter.encrypt(secret)},
- )
- )
- if recovery_codes:
- authenticators.append(
- Authenticator(
- user_id=totp.user_id,
- type="recovery_codes",
- data={
- "migrated_codes": [adapter.encrypt(c) for c in recovery_codes],
- },
- )
- )
- Authenticator.objects.bulk_create(authenticators)
- authenticators = []
- for user_key in UserKey.objects.filter(key_type="FIDO2").iterator():
- try:
- device = user_key.properties["device"]
- decoded_credential = websafe_decode(device)
- attested_credential = AttestedCredentialData(decoded_credential)
- aaguid = attested_credential.aaguid
- aaguid_bytes = aaguid.decode().encode()
- credential_id = attested_credential.credential_id
- public_key = attested_credential.public_key
- rp_id = settings.GLITCHTIP_URL.hostname
- rp_id_hash = hashlib.sha256(rp_id.encode("utf-8")).digest()
- flags = b"\x41" # 0x41: User present and attestation
- sign_count = b"\x00\x00\x00\x01"
- public_key_cbor = encode(public_key)
- auth_data = (
- rp_id_hash # 32-byte RP ID hash
- + flags # 1-byte flags
- + sign_count # 4-byte sign count
- + aaguid_bytes # 16-byte AAGUID
- + len(credential_id).to_bytes(2, "big")
- + credential_id # Credential ID length + Credential ID
- + public_key_cbor # CBOR-encoded public key
- )
- attestation_object = {
- "fmt": "none",
- "attStmt": {},
- "authData": auth_data,
- }
- attestation_object_cbor = encode(attestation_object)
- attestation_object_b64 = websafe_encode(attestation_object_cbor)
- new_credential_format = {
- "name": user_key.name,
- "credential": {
- "id": websafe_encode(attested_credential.credential_id),
- "type": "public-key",
- "rawId": websafe_encode(attested_credential.credential_id),
- "response": {
- "transports": ["usb"],
- # Fake
- "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiUU1zS0ZtWVFzZzRVZGVOMHdnLTg0YXAzcExiUTkzTGw5dURTQzFvbFhGcyIsIm9yaWdpbiI6Imh0dHA6Ly9sb2NhbGhvc3Q6ODAwMCIsImNyb3NzT3JpZ2luIjpmYWxzZX0",
- "attestationObject": attestation_object_b64,
- },
- "clientExtensionResults": {"credProps": {"rk": False}},
- "authenticatorAttachment": "cross-platform",
- },
- }
- authenticators.append(
- Authenticator(
- user_id=user_key.user_id,
- type="webauthn",
- data=new_credential_format,
- )
- )
- except Exception:
- continue
- Authenticator.objects.bulk_create(authenticators)
- class Migration(migrations.Migration):
- dependencies = [
- ("users", "0011_alter_user_email"),
- ]
- operations = [migrations.RunPython(migrate_mfa, migrations.RunPython.noop)]
|