diff --git a/homeassistant/components/homekit_controller/config_flow.py b/homeassistant/components/homekit_controller/config_flow.py
index 3f230d923c7acf61311df887cb33ae583c369919..507a5cbb70a92326a07907a2afaac418db51383e 100644
--- a/homeassistant/components/homekit_controller/config_flow.py
+++ b/homeassistant/components/homekit_controller/config_flow.py
@@ -2,6 +2,7 @@
 import json
 import logging
 import os
+import re
 
 import homekit
 from homekit.controller.ip_implementation import IpPairing
@@ -17,6 +18,8 @@ HOMEKIT_IGNORE = ["Home Assistant Bridge"]
 HOMEKIT_DIR = ".homekit"
 PAIRING_FILE = "pairing.json"
 
+PIN_FORMAT = re.compile(r"^(\d{3})-{0,1}(\d{2})-{0,1}(\d{3})$")
+
 _LOGGER = logging.getLogger(__name__)
 
 
@@ -59,6 +62,20 @@ def find_existing_host(hass, serial):
             return entry
 
 
+def ensure_pin_format(pin):
+    """
+    Ensure a pin code is correctly formatted.
+
+    Ensures a pin code is in the format 111-11-111. Handles codes with and without dashes.
+
+    If incorrect code is entered, an exception is raised.
+    """
+    match = PIN_FORMAT.search(pin)
+    if not match:
+        raise homekit.exceptions.MalformedPinError(f"Invalid PIN code f{pin}")
+    return "{}-{}-{}".format(*match.groups())
+
+
 @config_entries.HANDLERS.register(DOMAIN)
 class HomekitControllerFlowHandler(config_entries.ConfigFlow):
     """Handle a HomeKit config flow."""
@@ -277,6 +294,8 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow):
         if pair_info:
             code = pair_info["pairing_code"]
             try:
+                code = ensure_pin_format(code)
+
                 await self.hass.async_add_executor_job(self.finish_pairing, code)
 
                 pairing = self.controller.pairings.get(self.hkid)
@@ -284,6 +303,9 @@ class HomekitControllerFlowHandler(config_entries.ConfigFlow):
                     return await self._entry_from_accessory(pairing)
 
                 errors["pairing_code"] = "unable_to_pair"
+            except homekit.exceptions.MalformedPinError:
+                # Library claimed pin was invalid before even making an API call
+                errors["pairing_code"] = "authentication_error"
             except homekit.AuthenticationError:
                 # PairSetup M4 - SRP proof failed
                 # PairSetup M6 - Ed25519 signature verification failed
diff --git a/tests/components/homekit_controller/test_config_flow.py b/tests/components/homekit_controller/test_config_flow.py
index 56c1c30e8f353bfd5a8d21fdccdb68171c3f4c76..2a7f36ba470d84c515f1ed7ed9888b9a404498ef 100644
--- a/tests/components/homekit_controller/test_config_flow.py
+++ b/tests/components/homekit_controller/test_config_flow.py
@@ -27,6 +27,7 @@ PAIRING_START_ABORT_ERRORS = [
 ]
 
 PAIRING_FINISH_FORM_ERRORS = [
+    (homekit.exceptions.MalformedPinError, "authentication_error"),
     (homekit.MaxPeersError, "max_peers_error"),
     (homekit.AuthenticationError, "authentication_error"),
     (homekit.UnknownError, "unknown_error"),
@@ -37,6 +38,27 @@ PAIRING_FINISH_ABORT_ERRORS = [
     (homekit.AccessoryNotFoundError, "accessory_not_found_error")
 ]
 
+INVALID_PAIRING_CODES = [
+    "aaa-aa-aaa",
+    "aaa-11-aaa",
+    "111-aa-aaa",
+    "aaa-aa-111",
+    "1111-1-111",
+    "a111-11-111",
+    " 111-11-111",
+    "111-11-111 ",
+    "111-11-111a",
+    "1111111",
+]
+
+
+VALID_PAIRING_CODES = [
+    "111-11-111",
+    "123-45-678",
+    "11111111",
+    "98765432",
+]
+
 
 def _setup_flow_handler(hass):
     flow = config_flow.HomekitControllerFlowHandler()
@@ -56,6 +78,23 @@ async def _setup_flow_zeroconf(hass, discovery_info):
     return result
 
 
+@pytest.mark.parametrize("pairing_code", INVALID_PAIRING_CODES)
+def test_invalid_pairing_codes(pairing_code):
+    """Test ensure_pin_format raises for an invalid pin code."""
+    with pytest.raises(homekit.exceptions.MalformedPinError):
+        config_flow.ensure_pin_format(pairing_code)
+
+
+@pytest.mark.parametrize("pairing_code", VALID_PAIRING_CODES)
+def test_valid_pairing_codes(pairing_code):
+    """Test ensure_pin_format corrects format for a valid pin in an alternative format."""
+    valid_pin = config_flow.ensure_pin_format(pairing_code).split("-")
+    assert len(valid_pin) == 3
+    assert len(valid_pin[0]) == 3
+    assert len(valid_pin[1]) == 2
+    assert len(valid_pin[2]) == 3
+
+
 async def test_discovery_works(hass):
     """Test a device being discovered."""
     discovery_info = {
@@ -99,7 +138,7 @@ async def test_discovery_works(hass):
 
     # Pairing doesn't error error and pairing results
     flow.controller.pairings = {"00:00:00:00:00:00": pairing}
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "create_entry"
     assert result["title"] == "Koogeek-LS1-20833F"
     assert result["data"] == pairing.pairing_data
@@ -147,7 +186,7 @@ async def test_discovery_works_upper_case(hass):
     ]
 
     flow.controller.pairings = {"00:00:00:00:00:00": pairing}
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "create_entry"
     assert result["title"] == "Koogeek-LS1-20833F"
     assert result["data"] == pairing.pairing_data
@@ -196,7 +235,7 @@ async def test_discovery_works_missing_csharp(hass):
 
     flow.controller.pairings = {"00:00:00:00:00:00": pairing}
 
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "create_entry"
     assert result["title"] == "Koogeek-LS1-20833F"
     assert result["data"] == pairing.pairing_data
@@ -379,7 +418,7 @@ async def test_pair_unable_to_pair(hass):
     assert flow.controller.start_pairing.call_count == 1
 
     # Pairing doesn't error but no pairing object is generated
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "form"
     assert result["errors"]["pairing_code"] == "unable_to_pair"
 
@@ -486,7 +525,7 @@ async def test_pair_abort_errors_on_finish(hass, exception, expected):
 
     # User submits code - pairing fails but can be retried
     flow.finish_pairing.side_effect = exception("error")
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "abort"
     assert result["reason"] == expected
     assert flow.context == {
@@ -526,7 +565,7 @@ async def test_pair_form_errors_on_finish(hass, exception, expected):
 
     # User submits code - pairing fails but can be retried
     flow.finish_pairing.side_effect = exception("error")
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "form"
     assert result["errors"]["pairing_code"] == expected
     assert flow.context == {
@@ -639,7 +678,7 @@ async def test_user_works(hass):
     assert result["type"] == "form"
     assert result["step_id"] == "pair"
 
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "create_entry"
     assert result["title"] == "Koogeek-LS1-20833F"
     assert result["data"] == pairing.pairing_data
@@ -888,7 +927,7 @@ async def test_unignore_works(hass):
     assert flow.controller.start_pairing.call_count == 1
 
     # Pairing finalized
-    result = await flow.async_step_pair({"pairing_code": "111-22-33"})
+    result = await flow.async_step_pair({"pairing_code": "111-22-333"})
     assert result["type"] == "create_entry"
     assert result["title"] == "Koogeek-LS1-20833F"
     assert result["data"] == pairing.pairing_data