diff --git a/greg/tests/test_national_id.py b/greg/tests/test_national_id.py
new file mode 100644
index 0000000000000000000000000000000000000000..62815de91b7445394a222b87129e6df4e3bf7936
--- /dev/null
+++ b/greg/tests/test_national_id.py
@@ -0,0 +1,47 @@
+import pytest
+
+from greg.utils import is_valid_norwegian_national_id_number
+
+test_data = [
+    # Mostly random numbers from http://www.fnrinfo.no/Verktoy/FinnLovlige_Tilfeldig.aspx
+    ("13063626672", False, True),
+    ("09095114412", False, True),
+    ("02048538757", False, True),
+    ("12042335418", False, True),
+    ("16061739592", False, True),
+    ("05087648003", False, True),
+    ("22051602882", False, True),
+    ("11120618212", False, True),
+    ("19045838326", False, True),
+    ("17041579641", False, True),
+    ("20064632362", False, True),
+    ("12118913939", False, True),
+    ("04062141242", False, True),
+    ("10075049345", False, True),
+    ("17093749006", False, True),
+    ("07049226653", False, True),
+    ("25110899915", False, True),
+    ("01014000719", False, True),
+    ("19035205023", False, True),
+    ("12345678912", False, False),
+    ("410185", True, False),
+    # Test cases from https://github.com/navikt/fnrvalidator/blob/master/tests/fnr.spec.js
+    ("13097248022", False, True),
+    ("29029648784", False, True),
+    ("29020075838", False, True),
+    ("29020112345", False, False),
+    ("15021951940", False, True),
+    ("1234567890", False, False),
+    ("123456789101", False, False),
+    ("1234567891A", False, False),
+    ("13097248032", False, False),
+    ("13097248023", False, False),
+    ("32127248022", False, False),
+    ("13137248022", False, False),
+    ("53097248016", True, True),
+]
+
+
+@pytest.mark.parametrize("test_number, is_dnumber, valid", test_data)
+def test_register_guest(test_number, is_dnumber, valid):
+    assert is_valid_norwegian_national_id_number(test_number, is_dnumber) == valid
diff --git a/greg/utils.py b/greg/utils.py
index 8fd90ff1d89d8b025c1693dc07b94b0f2ed9273c..8408087a224f8877f6be4c619434eae081bccdce 100644
--- a/greg/utils.py
+++ b/greg/utils.py
@@ -1,6 +1,92 @@
 import re
+from datetime import date
 
 
 def camel_to_snake(s: str) -> str:
     """Turns `FooBar` into `foo_bar`."""
     return re.sub("([A-Z])", "_\\1", s).lower().lstrip("_")
+
+
+def is_valid_norwegian_national_id_number(input_digits: str, is_dnumber: bool) -> bool:
+    """
+    Checks whether input_digits is a valid national ID number of D-number.
+
+    It is based on the code found here:
+    https://github.com/navikt/fnrvalidator/blob/master/src/validator.js
+    """
+    return (
+        _check_correct_number_of_digits(input_digits)
+        and _compute_checksum(input_digits)
+        and _check_birthdate(input_digits, is_dnumber)
+    )
+
+
+def _check_correct_number_of_digits(input_digits: str) -> bool:
+    return re.search("^\\d{11}$", input_digits) is not None
+
+
+def _check_birthdate(digits: str, is_dnumber: bool) -> bool:
+    if is_dnumber:
+        # First number has been increased by 4, look here for an explanation of what the
+        # numbers in the D-number mean:
+        # https://www.skatteetaten.no/person/utenlandsk/norsk-identitetsnummer/d-nummer/
+        digits = str((int(digits[0:1]) - 4)) + digits[1:]
+
+    day = int(digits[0:2])
+    month = int(digits[2:4])
+    year = int(digits[4:6])
+
+    # Year 0 should mean 2000, not 1900
+    if year == 0:
+        year = 2000
+
+    try:
+        # Try to create a date object, it will fail is the date is not valid
+        date(year, month, day)
+    except ValueError:
+        # Not a valid date
+        return False
+
+    return True
+
+
+def _compute_checksum(input_digits: str) -> bool:
+    d = [int(s) for s in input_digits]
+    k1 = 11 - (
+        (
+            3 * d[0]
+            + 7 * d[1]
+            + 6 * d[2]
+            + 1 * d[3]
+            + 8 * d[4]
+            + 9 * d[5]
+            + 4 * d[6]
+            + 5 * d[7]
+            + 2 * d[8]
+        )
+        % 11
+    )
+
+    k2 = 11 - (
+        (
+            5 * d[0]
+            + 4 * d[1]
+            + 3 * d[2]
+            + 2 * d[3]
+            + 7 * d[4]
+            + 6 * d[5]
+            + 5 * d[6]
+            + 4 * d[7]
+            + 3 * d[8]
+            + 2 * k1
+        )
+        % 11
+    )
+
+    if k1 == 11:
+        k1 = 0
+
+    if k2 == 11:
+        k2 = 0
+
+    return k1 < 10 and k2 < 10 and k1 == d[9] and k2 == d[10]